diff --git a/web/pgadmin/about/__init__.py b/web/pgadmin/about/__init__.py
index f54418dd..acd7f117 100644
--- a/web/pgadmin/about/__init__.py
+++ b/web/pgadmin/about/__init__.py
@@ -10,6 +10,7 @@
 """A blueprint module implementing the about box."""
 MODULE_NAME = 'about'
 
+import os
 import sys
 
 from flask import Response, render_template, __version__, url_for
@@ -43,7 +44,7 @@ class AboutModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.about',
-            'path': url_for('about.index') + 'about',
+            'path': os.path.join(self.root_path, 'static/js/about'),
             'when': None
         }]
 
diff --git a/web/pgadmin/about/static/js/about.js b/web/pgadmin/about/static/js/about.js
new file mode 100644
index 00000000..a746d0f5
--- /dev/null
+++ b/web/pgadmin/about/static/js/about.js
@@ -0,0 +1,54 @@
+define(
+  ['jquery', 'alertify', 'pgadmin', 'underscore.string', 'sources/gettext',
+   'sources/url_for'
+  ],
+  function($, alertify, pgAdmin, S, gettext, url_for) {
+    pgAdmin = pgAdmin || window.pgAdmin || {};
+
+    /* Return back, this has been called more than once */
+    if (pgAdmin.About)
+        return;
+
+    pgAdmin.About = {
+      about_show: function() {
+        if (!alertify.aboutDialog) {
+          alertify.dialog('aboutDialog', function factory() {
+            return {
+              main: function(title, message) {
+                this.set('title', title);
+                this.message = message;
+              },
+              setup: function() {
+                return {
+                  buttons:[{ text: gettext("OK"), key: 27, className: "btn btn-primary" }],
+                  options: {
+                    modal: false,
+                    resizable: true,
+                    maximizable: true,
+                    pinnable: false,
+                    closableByDimmer: false
+                  }
+                };
+              },
+              build: function() {
+                alertify.pgDialogBuild.apply(this);
+              },
+              prepare:function() {
+                this.setContent(this.message);
+              }
+            };
+          });
+        }
+
+        var content = '';
+        $.get(url_for('about.index'),
+            function(data) {
+              alertify.aboutDialog(
+                  S(gettext("About %s")).sprintf(pgAdmin.Browser.utils.app_name).value(), data
+              ).resizeTo(800, 450);
+            });
+      }
+    };
+
+    return pgAdmin.About;
+  });
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 6ae14b44..21badc38 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -7,6 +7,7 @@
 #
 ##########################################################################
 
+import os
 import json
 from abc import ABCMeta, abstractmethod, abstractproperty
 
@@ -38,151 +39,55 @@ class BrowserModule(PgAdminModule):
 
     def get_own_stylesheets(self):
         stylesheets = []
-        # Add browser stylesheets
-        for (endpoint, filename) in [
-            ('static', 'vendor/codemirror/codemirror.css'),
-            ('static', 'vendor/codemirror/addon/dialog/dialog.css'),
-            ('static', 'vendor/jQuery-contextMenu/jquery.contextMenu.css' if current_app.debug
-            else 'vendor/jQuery-contextMenu/jquery.contextMenu.min.css'),
-            ('static', 'vendor/wcDocker/wcDocker.css' if current_app.debug
-            else 'vendor/wcDocker/wcDocker.min.css'),
-            ('browser.static', 'css/browser.css'),
-            ('browser.static', 'vendor/aciTree/css/aciTree.css')
-        ]:
-            stylesheets.append(url_for(endpoint, filename=filename))
         stylesheets.append(url_for('browser.browser_css'))
         return stylesheets
 
     def get_own_javascripts(self):
         scripts = list()
-        scripts.append({
-            'name': 'alertify',
-            'path': url_for(
-                'static',
-                filename='vendor/alertifyjs/alertify' if current_app.debug
-                else 'vendor/alertifyjs/alertify.min'
-            ),
-            'exports': 'alertify',
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jqueryui.position',
-            'path': url_for(
-                'static',
-                filename='vendor/jQuery-contextMenu/jquery.ui.position' if \
-                    current_app.debug else \
-                    'vendor/jQuery-contextMenu/jquery.ui.position.min'
-            ),
-            'deps': ['jquery'],
-            'exports': 'jQuery.ui.position',
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jquery.contextmenu',
-            'path': url_for(
-                'static',
-                filename='vendor/jQuery-contextMenu/jquery.contextMenu' if \
-                    current_app.debug else \
-                    'vendor/jQuery-contextMenu/jquery.contextMenu.min'
-            ),
-            'deps': ['jquery', 'jqueryui.position'],
-            'exports': 'jQuery.contextMenu',
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jquery.aciplugin',
-            'path': url_for(
-                'browser.static',
-                filename='vendor/aciTree/jquery.aciPlugin.min'
-            ),
-            'deps': ['jquery'],
-            'exports': 'aciPluginClass',
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jquery.acitree',
-            'path': url_for(
-                'browser.static',
-                filename='vendor/aciTree/jquery.aciTree' if
-                current_app.debug else 'vendor/aciTree/jquery.aciTree.min'
-            ),
-            'deps': ['jquery', 'jquery.aciplugin'],
-            'exports': 'aciPluginClass.plugins.aciTree',
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jquery.acisortable',
-            'path': url_for(
-                'browser.static',
-                filename='vendor/aciTree/jquery.aciSortable.min'
-            ),
-            'deps': ['jquery', 'jquery.aciplugin'],
-            'exports': 'aciPluginClass.plugins.aciSortable',
-            'when': None,
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'jquery.acifragment',
-            'path': url_for(
-                'browser.static',
-                filename='vendor/aciTree/jquery.aciFragment.min'
-            ),
-            'deps': ['jquery', 'jquery.aciplugin'],
-            'exports': 'aciPluginClass.plugins.aciFragment',
-            'when': None,
-            'preloaded': True
-        })
-        scripts.append({
-            'name': 'wcdocker',
-            'path': url_for(
-                'static',
-                filename='vendor/wcDocker/wcDocker' if current_app.debug
-                else 'vendor/wcDocker/wcDocker.min'
-            ),
-            'deps': ['jquery.contextmenu'],
-            'exports': '',
-            'preloaded': True
-        })
-
-        scripts.append({
-            'name': 'pgadmin.browser.datamodel',
-            'path': url_for('browser.static', filename='js/datamodel'),
-            'preloaded': True
-        })
 
+        # Define template JS to be loaded dynamically
         for name, script in [
-            ['pgadmin.browser', 'js/browser'],
             ['pgadmin.browser.endpoints', 'js/endpoints'],
-            ['pgadmin.browser.error', 'js/error']]:
-            scripts.append({
-                'name': name,
-                'path': url_for('browser.index') + script,
-                'preloaded': True
-            })
-
-        for name, script in [
-            ['pgadmin.browser.node', 'js/node'],
             ['pgadmin.browser.messages', 'js/messages'],
-            ['pgadmin.browser.collection', 'js/collection']]:
+            ['pgadmin.browser.utils', 'js/utils']
+        ]:
             scripts.append({
                 'name': name,
                 'path': url_for('browser.index') + script,
                 'preloaded': True,
-                'deps': ['pgadmin.browser.datamodel']
+                'is_template': True
             })
 
+        # Define static JS
         for name, end in [
             ['pgadmin.browser.menu', 'js/menu'],
             ['pgadmin.browser.panel', 'js/panel'],
-            ['pgadmin.browser.frame', 'js/frame']]:
+            ['pgadmin.browser.frame', 'js/frame'],
+            ['pgadmin.browser.datamodel', 'js/datamodel'],
+            ['pgadmin.browser.collection', 'js/collection'],
+            ['pgadmin.browser', 'js/browser'],
+            ['pgadmin.browser.wizard', 'js/wizard'],
+            ['pgadmin.browser.error', 'js/error'],
+        ]:
             scripts.append({
-                'name': name, 'path': url_for('browser.static', filename=end),
-                'preloaded': True})
+                'name': name,
+                'path': os.path.join(self.root_path, 'static/%s' % end),
+                'preloaded': True,
+                'is_template': False
+            })
+
+        scripts.append({
+            'name': 'pgadmin.browser.node',
+            'path': os.path.join(self.root_path, 'static/js/node'),
+            'deps': ['pgadmin.browser.datamodel'],
+            'is_template': False
+        })
 
         scripts.append({
             'name': 'pgadmin.browser.node.ui',
-            'path': url_for('browser.static', filename='js/node.ui'),
-            'when': 'server-group'
+            'path': os.path.join(self.root_path, 'static/js/node.ui'),
+            'when': 'server_group',
+            'is_template': False
         })
 
         for module in self.submodules:
@@ -298,9 +203,7 @@ class BrowserPluginModule(PgAdminModule):
         else:
             scripts.extend([{
                 'name': 'pgadmin.node.%s' % self.node_type,
-                'path': url_for(
-                    '%s.static'% self.name, filename=('js/%s' % self.node_type)
-                ),
+                'path': os.path.join(self.root_path, 'static/js/%s' % self.node_type),
                 'when': self.script_load,
                 'is_template': False
             }])
diff --git a/web/pgadmin/browser/collection.py b/web/pgadmin/browser/collection.py
index 0554f488..36dfec28 100644
--- a/web/pgadmin/browser/collection.py
+++ b/web/pgadmin/browser/collection.py
@@ -9,6 +9,7 @@
 
 from abc import ABCMeta, abstractmethod
 
+import os
 import six
 from flask import url_for, render_template
 from flask_babel import gettext
@@ -67,9 +68,7 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
         else:
             scripts.extend([{
                 'name': 'pgadmin.node.%s' % self.node_type,
-                'path': url_for(
-                    '%s.static'% self.name, filename=('js/%s' % self.node_type)
-                ),
+                'path': os.path.join(self.root_path, 'static/js/%s' % self.node_type),
                 'when': self.script_load,
                 'is_template': False
             }])
diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py
index ab0a3ee2..fd2b4ac9 100644
--- a/web/pgadmin/browser/server_groups/__init__.py
+++ b/web/pgadmin/browser/server_groups/__init__.py
@@ -26,7 +26,7 @@ from pgadmin.model import db, ServerGroup
 
 
 class ServerGroupModule(BrowserPluginModule):
-    NODE_TYPE = "server-group"
+    NODE_TYPE = "server_group"
 
     def get_nodes(self, *arg, **kwargs):
         """Return a JSON document listing the server groups for the user"""
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index a74436ef..01aead8d 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -7,6 +7,7 @@
 #
 ##########################################################################
 
+import os
 import simplejson as json
 import re
 import pgadmin.browser.server_groups as sg
@@ -129,13 +130,15 @@ class ServerModule(sg.ServerGroupPluginModule):
 
         scripts.extend([{
             'name': 'pgadmin.browser.server.privilege',
-            'path': url_for('%s.static'% self.name, filename='js/privilege'),
+            'path': os.path.join(self.root_path,
+                                 'static/js/privilege'),
             'when': self.node_type,
             'is_template': False,
             'deps': ['pgadmin.browser.node.ui']
         }, {
             'name': 'pgadmin.browser.server.variable',
-            'path': url_for('%s.static'% self.name, filename='js/variable'),
+            'path': os.path.join(self.root_path,
+                                 'static/js/variable'),
             'when': self.node_type,
             'is_template': False
         },{
diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py
index 7b2da993..c2512c43 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py
@@ -86,6 +86,14 @@ class ForeignServerModule(CollectionNodeModule):
         """
         return databases.DatabaseModule.NODE_TYPE
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = ForeignServerModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js
new file mode 100644
index 00000000..52cf4e90
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js
@@ -0,0 +1,184 @@
+define('pgadmin.node.foreign_server', [
+  'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+  'pgadmin.browser', 'alertify', 'pgadmin.browser.collection',
+  'pgadmin.browser.server.privilege'
+], function(gettext, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+  // Extend the browser's node model class to create a Options model
+  var OptionsModel = pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'fsrvoption',
+        defaults: {
+          fsrvoption: undefined,
+          fsrvvalue: undefined
+        },
+
+        // Defining schema for the Options model
+        schema: [
+          {id: 'fsrvoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true},
+          {id: 'fsrvvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true},
+        ],
+
+        /* validate function is used to validate the input given by
+         * the user. In case of error, message will be displayed on
+         * the browser for the respective control.
+         */
+        validate: function() {
+          // Validation for the option name
+          if (_.isUndefined(this.get('fsrvoption')) ||
+            _.isNull(this.get('fsrvoption')) ||
+            String(this.get('fsrvoption')).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = 'Please enter an option name';
+            this.errorModel.set('fsrvoption', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('fsrvoption');
+          }
+          return null;
+        }
+    });
+
+  // Extend the browser's collection class for foreign server collection
+  if (!pgBrowser.Nodes['coll-foreign_server']) {
+    var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-foreign_server'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'foreign_server',
+        label: gettext('Foreign Servers'),
+        type: 'coll-foreign_server',
+        columns: ['name','fsrvowner','description']
+      });
+  };
+
+  // Extend the browser's node class for foreign server node
+  if (!pgBrowser.Nodes['foreign_server']) {
+    pgAdmin.Browser.Nodes['foreign_server'] = pgAdmin.Browser.Node.extend({
+      parent_type: 'foreign_data_wrapper',
+      type: 'foreign_server',
+      sqlAlterHelp: 'sql-alterforeignserver.html',
+      sqlCreateHelp: 'sql-createforeignserver.html',
+      label: gettext('Foreign Server'),
+      hasSQL:  true,
+      hasDepends: true,
+      canDrop: true,
+      canDropCascade: true,
+      Init: function() {
+
+        // Avoid multiple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        /* Create foreign server context menu at database,
+         * foreign server collections and foreign server node
+         */
+        pgBrowser.add_menus([{
+          name: 'create_foreign_server_on_coll', node: 'coll-foreign_server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Server...'),
+          icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
+        },{
+          name: 'create_foreign_server', node: 'foreign_server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Server...'),
+          icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
+        },{
+          name: 'create_foreign_server', node: 'foreign_data_wrapper', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Server...'),
+          icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
+        }
+        ]);
+      },
+
+      // Defining model for foreign server node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          fsrvtype: undefined,
+          fsrvversion: undefined,
+          fsrvvalue: undefined,
+          fsrvoptions: [],
+          fsrvowner: undefined,
+          description: undefined,
+          fsrvacl: []
+        },
+
+        // Default values!
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+
+          if (isNew) {
+            var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+
+            this.set({'fsrvowner': userInfo.name}, {silent: true});
+          }
+          pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+        },
+
+        // Defining schema for the foreign server node
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', disabled: function(m) {
+            if (this.mode == 'edit' && this.node_info.server.version < 90200) {
+              return true;
+            }
+            else
+              return false;
+          }
+        },{
+          id: 'fsrvid', label: gettext('OID'), cell: 'string',
+          type: 'text', disabled: true, mode: ['properties']
+        },{
+          id: 'fsrvowner', label: gettext('Owner'), type: 'text',
+          control: Backform.NodeListByNameControl, node: 'role',
+          mode: ['edit', 'create', 'properties'], select2: { allowClear: false }
+        },{
+          id: 'fsrvtype', label: gettext('Type'), cell: 'string',
+          group: gettext('Definition'), type: 'text', mode: ['edit','create','properties'], disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'fsrvversion', label: gettext('Version'), cell: 'string',
+          group: gettext('Definition'), type: 'text'
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'fsrvoptions', label: 'Options', type: 'collection', group: gettext("Options"),
+          model: OptionsModel, control: 'unique-col-collection', mode: ['edit', 'create'],
+          canAdd: true, canDelete: true, uniqueCol : ['fsrvoption'],
+          columns: ['fsrvoption','fsrvvalue']
+         }, pgBrowser.SecurityGroupUnderSchema, {
+            id: 'fsrvacl', label: 'Privileges', type: 'collection', group: 'security',
+            model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), control: 'unique-col-collection',
+            mode: ['edit', 'create'], canAdd: true, canDelete: true, uniqueCol : ['grantee']
+         },{
+          id: 'acl', label: gettext('Privileges'), type: 'text',
+          group: gettext('Security'), mode: ['properties'], disabled: true
+         }
+        ],
+
+        /* validate function is used to validate the input given by
+         * the user. In case of error, message will be displayed on
+         * the browser for the respective control.
+         */
+        validate: function() {
+          var name = this.get('name');
+
+          if (_.isUndefined(name) || _.isNull(name) ||
+            String(name).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('name');
+          }
+          return null;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['coll-foreign_server'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/__init__.py
index c6a131df..263a4ca7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/__init__.py
@@ -101,6 +101,14 @@ class UserMappingModule(CollectionNodeModule):
 
         return servers.ServerModule.NODE_TYPE
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = UserMappingModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/js/user_mapping.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/js/user_mapping.js
new file mode 100644
index 00000000..7895ce17
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/js/user_mapping.js
@@ -0,0 +1,165 @@
+define('pgadmin.node.user_mapping', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+  'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+    // Extend the browser's node model class to create a Options model
+    var OptionsModel = pgAdmin.Browser.Node.Model.extend({
+       idAttribute: 'umoption',
+        defaults: {
+          umoption: undefined,
+          umvalue: undefined
+        },
+
+        // Defining schema for the Options model
+        schema: [{
+          id: 'umoption', label: gettext('Options'), type:'text',
+          cellHeaderClasses:'width_percent_50', group: null, editable: true
+        }, {
+          id: 'umvalue', label: gettext('Value'), type: 'text',
+          cellHeaderClasses:'width_percent_50', group:null, editable: true
+        }],
+
+        /* validate function is used to validate the input given by
+         * the user. In case of error, message will be displayed on
+         * the browser for the respective control.
+         */
+        validate: function() {
+          // Validation for the option value
+          if (_.isUndefined(this.get('umoption')) ||
+            _.isNull(this.get('umoption')) ||
+            String(this.get('umoption')).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = 'Please enter an option name';
+            this.errorModel.set('umoption', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('umoption');
+          }
+          return null;
+        }
+    });
+
+  // Extend the browser's collection class for user mapping collection
+  if (!pgBrowser.Nodes['coll-user_mapping']) {
+    var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-user_mapping'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'user_mapping',
+        label: gettext('User Mappings'),
+        type: 'coll-user_mapping',
+        columns: ['name']
+      });
+  };
+
+  // Extend the browser's node class for user mapping node
+  if (!pgBrowser.Nodes['user_mapping']) {
+    pgAdmin.Browser.Nodes['user_mapping'] = pgAdmin.Browser.Node.extend({
+      parent_type: 'foreign_server',
+      type: 'user_mapping',
+      sqlAlterHelp: 'sql-alterusermapping.html',
+      sqlCreateHelp: 'sql-createusermapping.html',
+      dialogHelp: url_for('help.static', {'filename': 'user_mapping_dialog.html'}),
+      label: gettext('User Mapping'),
+      hasSQL:  true,
+      hasDepends: true,
+      canDrop: true,
+      canDropCascade: true,
+      Init: function() {
+
+        // Avoid multiple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        /* Create foreign server context menu at database,
+         * user mapping collections and user mapping node
+         */
+        pgBrowser.add_menus([{
+          name: 'create_user_mapping_on_coll', node: 'coll-user_mapping', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('User Mapping...'),
+          icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
+        },{
+          name: 'create_user_mapping', node: 'user_mapping', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('User Mapping...'),
+          icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
+        },{
+          name: 'create_user_mapping', node: 'foreign_server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('User Mapping...'),
+          icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
+        }
+        ]);
+      },
+
+      // Defining model for user mapping node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          um_options: []
+        },
+
+        // Default values!
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+
+          if (isNew) {
+            var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+
+            this.set({'name': userInfo.name}, {silent: true});
+          }
+          pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+        },
+
+        // Defining schema for the user mapping node
+        schema: [{
+          id: 'name', label: gettext('User'), type: 'text',
+          control: Backform.NodeListByNameControl, node: 'role',
+          mode: ['edit', 'create', 'properties'], select2: { allowClear: false },
+          disabled: function(m) { return !m.isNew(); },
+          transform: function(data) {
+            var self = this;
+            node = self.field.get('schema_node');
+            var res =
+            Backform.NodeListByNameControl.prototype.defaults.transform.apply(
+              this, arguments
+            );
+            res.unshift({label: 'CURRENT_USER', value: 'CURRENT_USER', image: 'icon-' + node.type},
+                        {label: 'PUBLIC', value: 'PUBLIC', image: 'icon-' + node.type});
+            return res;
+         }
+        },{
+          id: 'um_oid', label: gettext('OID'), cell: 'string',
+          type: 'text', disabled: true, mode: ['properties'],
+        },{
+          id: 'umoptions', label: 'Options', type: 'collection', group: gettext("Options"),
+          model: OptionsModel, control: 'unique-col-collection', mode: ['create', 'edit'],
+          canAdd: true, canDelete: true, uniqueCol : ['umoption']
+        }
+        ],
+
+        /* validate function is used to validate the input given by
+         * the user. In case of error, message will be displayed on
+         * the browser for the respective control.
+         */
+        validate: function() {
+          var name = this.get('name');
+
+          if (_.isUndefined(name) || _.isNull(name) ||
+            String(name).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('name');
+          }
+          return null;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['coll-user_mapping'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/__init__.py
index ea2254d8..eebf05b4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/__init__.py
@@ -94,6 +94,14 @@ class CatalogObjectColumnsModule(CollectionNodeModule):
             'show_system_objects'
         )
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = CatalogObjectColumnsModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/static/js/catalog_object_column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/static/js/catalog_object_column.js
new file mode 100644
index 00000000..b5138576
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/static/js/catalog_object_column.js
@@ -0,0 +1,71 @@
+define('pgadmin.node.catalog_object_column', [
+  'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+  'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'
+], function(gettext, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-catalog_object_column']) {
+    var databases = pgAdmin.Browser.Nodes['coll-catalog_object_column'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'catalog_object_column',
+        label: gettext('catalog_object_column'),
+        type: 'coll-catalog_object_column',
+        columns: ['attname', 'attnum', 'cltype', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['catalog_object_column']) {
+    pgAdmin.Browser.Nodes['catalog_object_column'] =
+      pgAdmin.Browser.Node.extend({
+      parent_type: 'catalog_object',
+      type: 'catalog_object_column',
+      label: gettext('catalog_object_column'),
+      hasSQL:  false,
+      hasScriptTypes: [],
+      hasDepends: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          attname: undefined,
+          attowner: undefined,
+          atttypid: undefined,
+          attnum: undefined,
+          cltype: undefined,
+          collspcname: undefined,
+          attacl: undefined,
+          description: undefined
+        },
+        schema: [{
+          id: 'attname', label: gettext('Column'), cell: 'string',
+          type: 'text', disabled: true
+        },{
+          id: 'attowner', label: gettext('Owner'), cell: 'string',
+          type: 'text', disabled: true
+        },{
+          id: 'attnum', label: gettext('Position'), cell: 'string',
+          type: 'text', disabled: true
+        },{
+          id: 'cltype', label: gettext('Data type'), cell: 'string',
+          group: gettext('Definition'), type: 'text', disabled: true
+        },{
+          id: 'collspcname', label: gettext('Collation'), cell: 'string',
+          group: gettext('Definition'), type: 'text', disabled: true
+        },{
+          id: 'attacl', label: gettext('Privileges'), cell: 'string',
+          group: gettext('Security'), type: 'text', disabled: true
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', disabled: true
+        }]
+      })
+    });
+  }
+
+  return pgBrowser.Nodes['catalog_object_column'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py
index 2ee6a430..ed383942 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py
@@ -56,7 +56,7 @@ class ForeignTableModule(SchemaChildModule):
       - Load the module script for Foreign Table, when schema node is
         initialized.
     """
-    NODE_TYPE = 'foreign-table'
+    NODE_TYPE = 'foreign_table'
     COLLECTION_LABEL = gettext("Foreign Tables")
 
     def __init__(self, *args, **kwargs):
@@ -396,7 +396,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
                     row['oid'],
                     scid,
                     row['name'],
-                    icon="icon-foreign-table"
+                    icon="icon-foreign_table"
                 ))
 
         return make_json_response(
@@ -430,7 +430,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
                     row['oid'],
                     scid,
                     row['name'],
-                    icon="icon-foreign-table"
+                    icon="icon-foreign_table"
                 ),
                 status=200
             )
@@ -689,7 +689,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
                     foid,
                     scid,
                     self.request['name'],
-                    icon="icon-foreign-table"
+                    icon="icon-foreign_table"
                 )
             )
         except Exception as e:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/coll-foreign_table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/coll-foreign_table.png
new file mode 100644
index 00000000..dc1f742b
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/coll-foreign_table.png differ
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/foreign_table.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/foreign_table.png
new file mode 100644
index 00000000..53c13388
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/img/foreign_table.png differ
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
new file mode 100644
index 00000000..32d5072c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
@@ -0,0 +1,719 @@
+/* Create and Register Foreign Table Collection and Node. */
+define('pgadmin.node.foreign_table', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-foreign_table']) {
+    var foreigntable = pgBrowser.Nodes['coll-foreign_table'] =
+      pgBrowser.Collection.extend({
+        node: 'foreign_table',
+        label: gettext('Foreign Tables'),
+        type: 'coll-foreign_table',
+        columns: ['name', 'owner', 'description']
+      });
+  };
+
+  // Integer Cell for Columns Length and Precision
+  var IntegerDepCell = Backgrid.IntegerCell.extend({
+      initialize: function() {
+        Backgrid.NumberCell.prototype.initialize.apply(this, arguments);
+        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
+      },
+      dependentChanged: function () {
+        this.$el.empty();
+        var model = this.model;
+        var column = this.column;
+        var editable = this.column.get("editable");
+        var is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+
+        if (is_editable){ this.$el.addClass("editable"); }
+        else { this.$el.removeClass("editable"); }
+
+        this.delegateEvents();
+        return this;
+      },
+      remove: Backgrid.Extension.DependentCell.prototype.remove
+    });
+
+
+  // Options Model
+  var ColumnOptionsModel = pgBrowser.Node.Model.extend({
+    idAttribute: 'option',
+    defaults: {
+      option: undefined,
+      value: undefined
+    },
+    schema: [
+      {id: 'option', label:'Option', type:'text', editable: true, cellHeaderClasses: 'width_percent_30'},
+      {
+        id: 'value', label:'Value', type: 'text', editable: true, cellHeaderClasses: 'width_percent_50'
+      }
+    ],
+    validate: function() {
+      if (_.isUndefined(this.get('value')) ||
+          _.isNull(this.get('value')) ||
+          String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+        var msg = 'Please enter a value.';
+
+        this.errorModel.set('value', msg);
+
+        return msg;
+      } else {
+        this.errorModel.unset('value');
+      }
+
+      return null;
+    }
+  });
+
+  // Columns Model
+  var ColumnsModel = pgBrowser.Node.Model.extend({
+    idAttribute: 'attnum',
+    defaults: {
+      attname: undefined,
+      datatype: undefined,
+      typlen: undefined,
+      precision: undefined,
+      typdefault: undefined,
+      attnotnull: undefined,
+      collname: undefined,
+      attnum: undefined,
+      inheritedfrom: undefined,
+      inheritedid: undefined,
+      attstattarget: undefined,
+      coloptions: []
+    },
+    type_options: undefined,
+    schema: [{
+        id: 'attname', label: gettext('Name'), cell: 'string', type: 'text',
+        editable: 'is_editable_column', cellHeaderClasses: 'width_percent_40'
+      },{
+        id: 'datatype', label: gettext('Data Type'), cell: 'node-ajax-options',
+        control: 'node-ajax-options', type: 'text', url: 'get_types',
+        editable: 'is_editable_column', cellHeaderClasses: 'width_percent_0',
+        group: gettext('Definition'),
+        transform: function(d, self){
+            self.model.type_options = d;
+            return d;
+          }
+      },{
+        id: 'typlen', label: gettext('Length'),
+        cell: 'string', group: gettext('Definition'),
+        type: 'int', deps: ['datatype'],
+        disabled: function(m) {
+          var val = m.get('typlen');
+          // We will store type from selected from combobox
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) {
+
+            if (!_.isUndefined(val)) {
+              setTimeout(function() {
+                m.set('typlen', undefined);
+              }, 10);
+            }
+            return true;
+          }
+
+          var of_type = m.get('datatype'),
+              has_length = false;
+          if(m.type_options) {
+            m.set('is_tlength', false, {silent: true});
+
+            // iterating over all the types
+            _.each(m.type_options, function(o) {
+              // if type from selected from combobox matches in options
+              if ( of_type == o.value ) {
+                // if length is allowed for selected type
+                if(o.length)
+                {
+                  // set the values in model
+                  has_length = true;
+                  m.set('is_tlength', true, {silent: true});
+                  m.set('min_val', o.min_val, {silent: true});
+                  m.set('max_val', o.max_val, {silent: true});
+                }
+              }
+            });
+
+            if (!has_length && !_.isUndefined(val)) {
+              setTimeout(function() {
+                m.set('typlen', undefined);
+              }, 10);
+            }
+
+            return !(m.get('is_tlength'));
+          }
+          if (!has_length && !_.isUndefined(val)) {
+              setTimeout(function() {
+                m.set('typlen', undefined);
+              }, 10);
+            }
+          return true;
+        },
+        cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'precision', label: gettext('Precision'),
+        type: 'int', deps: ['datatype'],
+        cell: 'string', group: gettext('Definition'),
+        disabled: function(m) {
+          var val = m.get('precision');
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) {
+
+            if (!_.isUndefined(val)) {
+              setTimeout(function() {
+                m.set('precision', undefined);
+              }, 10);
+            }
+            return true;
+          }
+
+          var of_type = m.get('datatype'),
+              has_precision = false;
+
+          if(m.type_options) {
+             m.set('is_precision', false, {silent: true});
+            // iterating over all the types
+            _.each(m.type_options, function(o) {
+              // if type from selected from combobox matches in options
+              if ( of_type == o.value ) {
+                // if precession is allowed for selected type
+                if(o.precision)
+                {
+                  has_precision = true;
+                  // set the values in model
+                  m.set('is_precision', true, {silent: true});
+                  m.set('min_val', o.min_val, {silent: true});
+                  m.set('max_val', o.max_val, {silent: true});
+                }
+              }
+            });
+            if (!has_precision && !_.isUndefined(val)) {
+              setTimeout(function() {
+                m.set('precision', undefined);
+              }, 10);
+            }
+            return !(m.get('is_precision'));
+          }
+          if (!has_precision && !_.isUndefined(val)) {
+            setTimeout(function() {
+              m.set('precision', undefined);
+            }, 10);
+          }
+          return true;
+      }, cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'typdefault', label: gettext('Default'), type: 'text',
+        cell: 'string', min_version: 90300, group: gettext('Definition'),
+        placeholder: "Enter an expression or a value.",
+        cellHeaderClasses: 'width_percent_10',
+        editable: function(m) {
+          if(!(_.isUndefined(m.get('inheritedid'))
+            || _.isNull(m.get('inheritedid'))
+            || _.isUndefined(m.get('inheritedfrom'))
+            || _.isNull(m.get('inheritedfrom')))) { return false; }
+          if (this.get('node_info').server.version < 90300){
+            return false;
+          }
+          return true;
+        }
+      },{
+        id: 'attnotnull', label: gettext('Not Null'),
+        cell: 'boolean',type: 'switch', editable: 'is_editable_column',
+        cellHeaderClasses: 'width_percent_10', group: gettext('Definition')
+      },{
+        id: 'attstattarget', label: gettext('Statistics'), min_version: 90200,
+        cell: 'integer', type: 'int', group: gettext('Definition'),
+        editable: function(m) {
+         if (_.isUndefined(m.isNew) || m.isNew()) { return false; }
+         if (this.get('node_info').server.version < 90200){
+            return false;
+         }
+         return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+          || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+        }, cellHeaderClasses: 'width_percent_10'
+      },{
+        id: 'collname', label: gettext('Collation'), cell: 'node-ajax-options',
+        control: 'node-ajax-options', type: 'text', url: 'get_collations',
+        min_version: 90300, editable: function(m) {
+          if (!(_.isUndefined(m.isNew)) && !m.isNew()) { return false; }
+          return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+           || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+        },
+        cellHeaderClasses: 'width_percent_20', group: gettext('Definition')
+      },{
+        id: 'attnum', cell: 'string',type: 'text', visible: false
+      },{
+        id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'string',
+        type: 'text', visible: false, mode: ['properties', 'edit'],
+        cellHeaderClasses: 'width_percent_10'
+      },{
+          id: 'coloptions', label: gettext('Options'), cell: 'string',
+          type: 'collection', group: gettext('Options'), mode: ['edit', 'create'],
+          model: ColumnOptionsModel, canAdd: true, canDelete: true, canEdit: false,
+          control: Backform.UniqueColCollectionControl, uniqueCol : ['option'],
+          min_version: 90200
+      }],
+    validate: function() {
+      var errmsg = null;
+
+      if (_.isUndefined(this.get('attname')) || String(this.get('attname')).replace(/^\s+|\s+$/g, '') == '') {
+        errmsg = gettext('Column Name cannot be empty!');
+        this.errorModel.set('attname', errmsg);
+      } else {
+        this.errorModel.unset('attname');
+      }
+
+      if (_.isUndefined(this.get('datatype')) || String(this.get('datatype'))
+      .replace(/^\s+|\s+$/g, '') == '') {
+        errmsg = gettext('Column Datatype cannot be empty!');
+        this.errorModel.set('datatype', errmsg);
+      } else {
+        this.errorModel.unset('datatype');
+      }
+
+      return errmsg;
+    },
+    is_editable_column: function(m) {
+      return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+       || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  /* NodeAjaxOptionsMultipleControl is for multiple selection of Combobox.
+  *  This control is used to select Multiple Parent Tables to be inherited.
+  *  It also populates/vacates Columns on selection/deselection of the option (i.e. table name).
+  *  To populates the column, it calls the server and fetch the columns data
+  *  for the selected table.
+  */
+  var NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsControl.extend({
+    onChange: function(e) {
+      var model = this.model,
+          $el = $(e.target),
+          attrArr = this.field.get("name").split('.'),
+          name = attrArr.shift(),
+          path = attrArr.join('.'),
+          value = this.getValueFromDOM(),
+          changes = {},
+          columns = model.get('columns'),
+          inherits = model.get(name);
+
+      if (this.model.errorModel instanceof Backbone.Model) {
+        if (_.isEmpty(path)) {
+          this.model.errorModel.unset(name);
+        } else {
+          var nestedError = this.model.errorModel.get(name);
+          if (nestedError) {
+            this.keyPathSetter(nestedError, path, null);
+            this.model.errorModel.set(name, nestedError);
+          }
+        }
+      }
+
+      var self = this;
+
+      if (typeof(inherits)  == "string"){ inherits = JSON.parse(inherits); }
+
+      // Remove Columns if inherit option is deselected from the combobox
+      if(_.size(value) < _.size(inherits)) {
+        var dif =  _.difference(inherits, value);
+        var rmv_columns = columns.where({inheritedid: parseInt(dif[0])});
+        columns.remove(rmv_columns);
+      }
+      else
+      {
+        _.each(value, function(i) {
+          // Fetch Columns from server
+          var fnd_columns = columns.where({inheritedid: parseInt(i)});
+          if (fnd_columns && fnd_columns.length <= 0) {
+            inhted_columns = self.fetchColumns(i);
+            columns.add(inhted_columns);
+          }
+        });
+      }
+
+      changes[name] = _.isEmpty(path) ? value : _.clone(model.get(name)) || {};
+      this.stopListening(this.model, "change:" + name, this.render);
+      model.set(changes);
+      this.listenTo(this.model, "change:" + name, this.render);
+    },
+    fetchColumns: function(table_id){
+      var self = this,
+          url = 'get_columns',
+          m = self.model.top || self.model;
+
+      if (url) {
+        var node = this.field.get('schema_node'),
+            node_info = this.field.get('node_info'),
+            full_url = node.generate_url.apply(
+              node, [
+                null, url, this.field.get('node_data'),
+                this.field.get('url_with_id') || false, node_info
+              ]),
+            cache_level = this.field.get('cache_level') || node.type,
+            cache_node = this.field.get('cache_node');
+
+        cache_node = (cache_node && pgBrowser.Nodes['cache_node']) || node;
+
+        m.trigger('pgadmin:view:fetching', m, self.field);
+        var data = {attrelid: table_id}
+
+        // Fetching Columns data for the selected table.
+        $.ajax({
+          async: false,
+          url: full_url,
+          data: data,
+          success: function(res) {
+            /*
+             * We will cache this data for short period of time for avoiding
+             * same calls.
+             */
+            data = cache_node.cache(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) || [];
+        return data;
+      }
+    },
+  });
+
+
+  // Constraints Model
+  var ConstraintModel = pgBrowser.Node.Model.extend({
+    idAttribute: 'conoid',
+    initialize: function(attrs, args) {
+      var isNew = (_.size(attrs) === 0);
+      if (!isNew) {
+        this.convalidated_default = this.get('convalidated')
+      }
+      pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
+    },
+    defaults: {
+      conoid: undefined,
+      conname: undefined,
+      consrc: undefined,
+      connoinherit: undefined,
+      convalidated: true,
+      conislocal: undefined
+    },
+    convalidated_default: true,
+    schema: [{
+      id: 'conoid', type: 'text', cell: 'string', visible: false
+    },{
+      id: 'conname', label: gettext('Name'), type: 'text', cell: 'string',
+      editable: 'is_editable', cellHeaderClasses: 'width_percent_30'
+    },{
+      id: 'consrc', label: gettext('Check'), type: 'multiline',
+      editable: 'is_editable', cell: Backgrid.Extension.TextareaCell,
+      cellHeaderClasses: 'width_percent_30'
+    },{
+      id: 'connoinherit', label: gettext('No Inherit'), type: 'switch',
+      cell: 'boolean', editable: 'is_editable',
+      cellHeaderClasses: 'width_percent_20'
+    },{
+      id: 'convalidated', label: gettext('Validate?'), type: 'switch',
+      cell: 'boolean', cellHeaderClasses: 'width_percent_20',
+      editable: function(m) {
+        var server = this.get('node_info').server;
+        if (_.isUndefined(m.isNew)) { return true; }
+        if (!m.isNew()) {
+          if(m.get('convalidated') && m.convalidated_default) {
+            return false;
+          }
+          return true;
+        }
+        return true;
+      }
+     }
+    ],
+    validate: function() {
+      var err = {},
+      errmsg;
+
+      if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') {
+        err['conname'] = gettext('Constraint Name cannot be empty!');
+        errmsg = errmsg || err['conname'];
+      }
+
+      if (_.isUndefined(this.get('consrc')) || String(this.get('consrc'))
+      .replace(/^\s+|\s+$/g, '') == '') {
+        err['consrc'] = gettext('Constraint Check cannot be empty!');
+        errmsg = errmsg || err['consrc'];
+      }
+
+      this.errorModel.clear().set(err);
+
+      return errmsg;
+    },
+    is_editable: function(m) {
+        return _.isUndefined(m.isNew) ? true : m.isNew();
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  // Options Model
+  var OptionsModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      option: undefined,
+      value: undefined
+    },
+    schema: [{
+      id: 'option', label: gettext('Option'), cell: 'string', type: 'text',
+      editable: true, cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'value', label: gettext('Value'), cell: 'string',type: 'text',
+      editable: true, cellHeaderClasses:'width_percent_50'
+    }
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  if (!pgBrowser.Nodes['foreign_table']) {
+    pgBrowser.Nodes['foreign_table'] = pgBrowser.Node.extend({
+      type: 'foreign_table',
+      sqlAlterHelp: 'sql-alterforeigntable.html',
+      sqlCreateHelp: 'sql-createforeigntable.html',
+      dialogHelp: url_for('help.static', {'filename': 'foreign_table_dialog.html'}),
+      label: gettext('Foreign Table'),
+      collection_type: 'coll-foreign_table',
+      hasSQL: true,
+      hasDepends: true,
+      hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
+      parent_type: ['schema'],
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_foreign-table_on_coll', node: 'coll-foreign_table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Table...'),
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'foreign_table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Table...'),
+          icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_foreign-table', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign Table...'),
+          icon: 'wcTabIcon icon-foreign_table', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        }
+        ]);
+
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgBrowser.Node.Model.extend({
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+          if (isNew) {
+            var schema = args.node_info.schema._label,
+                userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+
+            // Set Selected Schema and Current User
+            this.set({
+              'basensp': schema, 'owner': userInfo.name
+            }, {silent: true});
+          }
+          pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
+        },
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          owner: undefined,
+          basensp: undefined,
+          description: undefined,
+          ftsrvname: undefined,
+          strftoptions: undefined,
+          inherits: [],
+          columns: [],
+          constraints: [],
+          ftoptions: [],
+          relacl: [],
+          stracl: [],
+          seclabels: []
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'owner', label: gettext('Owner'), cell: 'string',
+          control: Backform.NodeListByNameControl,
+          node: 'role',  type: 'text', select2: { allowClear: false }
+        },{
+          id: 'basensp', label: gettext('Schema'), cell: 'node-list-by-name',
+           control: 'node-list-by-name', cache_level: 'database', type: 'text',
+           node: 'schema', mode:['create', 'edit']
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'ftsrvname', label: gettext('Foreign server'), cell: 'string', control: 'node-ajax-options',
+          type: 'text', group: gettext('Definition'), url: 'get_foreign_servers', disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'inherits', label: gettext('Inherits'), group: gettext('Definition'),
+          type: 'array', min_version: 90500, control: NodeAjaxOptionsMultipleControl,
+          url: 'get_tables', select2: {multiple: true},
+          'cache_level': 'database',
+          transform: function(d, self){
+            if (this.field.get('mode') == 'edit') {
+              var oid = this.model.get('oid');
+              var s = _.findWhere(d, {'id': oid});
+              if (s) {
+                d = _.reject(d, s);
+              }
+            }
+            return d;
+          }
+        },{
+          id: 'columns', label: gettext('Columns'), cell: 'string',
+          type: 'collection', group: gettext('Columns'), visible: false, mode: ['edit', 'create'],
+          model: ColumnsModel, canAdd: true, canDelete: true, canEdit: true,
+          columns: ['attname', 'datatype', 'inheritedfrom'],
+          canDeleteRow: function(m) {
+            return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+              || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+          },
+          canEditRow: function(m) {
+            return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
+              || _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
+          }
+        },
+        {
+          id: 'constraints', label: gettext('Constraints'), cell: 'string',
+          type: 'collection', group: gettext('Constraints'), visible: false, mode: ['edit', 'create'],
+          model: ConstraintModel, canAdd: true, canDelete: true, columns: ['conname','consrc', 'connoinherit', 'convalidated'],
+          canEdit: function(o) {
+            if (o instanceof Backbone.Model) {
+              if (o instanceof ConstraintModel) {
+                return o.isNew();
+              }
+            }
+            return true;
+          }, min_version: 90500, canDeleteRow: function(m) {
+            return (m.get('conislocal') == true || _.isUndefined(m.get('conislocal'))) ? true : false
+          }
+        },{
+          id: 'strftoptions', label: gettext('Options'), cell: 'string',
+          type: 'text', group: gettext('Definition'), mode: ['properties']
+        },{
+          id: 'ftoptions', label: gettext('Options'), cell: 'string',
+          type: 'collection', group: gettext('Options'), mode: ['edit', 'create'],
+          model: OptionsModel, canAdd: true, canDelete: true, canEdit: false,
+          control: 'unique-col-collection', uniqueCol : ['option']
+        },{
+          id: 'relacl', label: gettext('Privileges'), cell: 'string',
+          type: 'text', group: gettext('Security'),
+          mode: ['properties'], min_version: 90200
+        }, pgBrowser.SecurityGroupUnderSchema, {
+          id: 'acl', label: gettext('Privileges'), model: pgAdmin
+          .Browser.Node.PrivilegeRoleModel.extend(
+          {privileges: ['a','r','w','x']}), uniqueCol : ['grantee', 'grantor'],
+          editable: false, type: 'collection', group: 'security',
+          mode: ['edit', 'create'],
+          canAdd: true, canDelete: true, control: 'unique-col-collection',
+          min_version: 90200
+        },{
+          id: 'seclabels', label: gettext('Security Labels'),
+          model: pgBrowser.SecLabelModel, type: 'collection',
+          group: 'security', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true,
+          control: 'unique-col-collection', uniqueCol : ['provider']
+        }
+        ],
+        validate: function()
+        {
+          var err = {},
+              errmsg,
+              seclabels = this.get('seclabels');
+
+          if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = gettext('Name cannot be empty.');
+            errmsg = errmsg || err['name'];
+          }
+
+          if (_.isUndefined(this.get('basensp')) || String(this.get('basensp'))
+          .replace(/^\s+|\s+$/g, '') == '') {
+            err['basensp'] = gettext('Schema cannot be empty.');
+            errmsg = errmsg || err['basensp'];
+          }
+
+          if (_.isUndefined(this.get('ftsrvname')) || String(this.get('ftsrvname')).replace(/^\s+|\s+$/g, '') == '') {
+            err['ftsrvname'] = gettext('Foreign server cannot be empty.');
+            errmsg = errmsg || err['ftsrvname'];
+          }
+
+          this.errorModel.clear().set(err);
+
+          return null;
+        }
+      }),
+      canCreate: function(itemData, item, data) {
+        //If check is false then , we will allow create menu
+        if (data && data.check == false)
+          return true;
+
+        var t = pgBrowser.tree, i = item, d = itemData;
+        // To iterate over tree to check parent node
+        while (i) {
+          // If it is schema then allow user to create foreign table
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-foreign-table' == d._type) {
+            //Check if we are not child of catalog
+            var prev_i = t.hasParent(i) ? t.parent(i) : null,
+              prev_d = prev_i ? t.itemData(prev_i) : null;
+            if( prev_d._type == 'catalog') {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+        // by default we do not want to allow create menu
+        return true;
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['foreign_table'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_add.py
index d11e11c2..3dcf456c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_add.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_add.py
@@ -30,7 +30,7 @@ class ForeignTableAddTestCase(BaseTestGenerator):
 
     scenarios = [
         # Fetching default URL for foreign server node.
-        ('Check foreign table Node', dict(url='/browser/foreign-table/obj/'))
+        ('Check foreign table Node', dict(url='/browser/foreign_table/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_delete.py
index 86ea0ae6..731e32b0 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_delete.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_delete.py
@@ -30,7 +30,7 @@ class ForeignTableDeleteTestCase(BaseTestGenerator):
 
     scenarios = [
         # Fetching default URL for foreign table node.
-        ('Check foreign table Node', dict(url='/browser/foreign-table/obj/'))
+        ('Check foreign table Node', dict(url='/browser/foreign_table/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_get.py
index 7c4faf1c..329bd34a 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_get.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_get.py
@@ -29,7 +29,7 @@ class ForeignTableGetTestCase(BaseTestGenerator):
     """
     scenarios = [
         # Fetching default URL for foreign server node.
-        ('Check foreign table Node', dict(url='/browser/foreign-table/obj/'))
+        ('Check foreign table Node', dict(url='/browser/foreign_table/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_put.py
index b46e1844..ee219047 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_put.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/tests/test_foreign_table_put.py
@@ -30,7 +30,7 @@ class ForeignTablePutTestCase(BaseTestGenerator):
     """
     scenarios = [
         # Fetching default URL for foreign server node.
-        ('Check foreign table Node', dict(url='/browser/foreign-table/obj/'))
+        ('Check foreign table Node', dict(url='/browser/foreign_table/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py
index 4d34f131..42187b0d 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py
@@ -109,6 +109,14 @@ class EdbFuncModule(CollectionNodeModule):
             self.SHOW_ON_BROWSER, category_label=gettext('Nodes')
         )
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = EdbFuncModule(__name__)
 
@@ -693,6 +701,14 @@ class EdbProcModule(CollectionNodeModule):
             self.SHOW_ON_BROWSER, category_label=gettext('Nodes')
         )
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 procedure_blueprint = EdbProcModule(__name__)
 
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js
new file mode 100644
index 00000000..7ea2b397
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js
@@ -0,0 +1,112 @@
+/* Create and Register Function Collection and Node. */
+define('pgadmin.node.edbfunc', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.browser.collection', 'pgadmin.browser.server.privilege'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-edbfunc']) {
+    pgBrowser.Nodes['coll-edbfunc'] =
+      pgBrowser.Collection.extend({
+        node: 'edbfunc',
+        label: gettext('Functions'),
+        type: 'coll-edbfunc',
+        columns: ['name', 'funcowner', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['edbfunc']) {
+    pgBrowser.Nodes['edbfunc'] = pgBrowser.Node.extend({
+      type: 'edbfunc',
+      dialogHelp: url_for('help.static', {'filename': 'edbfunc_dialog.html'}),
+      label: gettext('Function'),
+      collection_type: 'coll-edbfunc',
+      hasDepends: true,
+      canEdit: false,
+      hasSQL: true,
+      hasScriptTypes: [],
+      parent_type: ['package'],
+      Init: function(args) {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+      },
+      canDrop: false,
+      canDropCascade: false,
+      model: pgBrowser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          funcowner: undefined,
+          pronargs: undefined, /* Argument Count */
+          proargs: undefined, /* Arguments */
+          proargtypenames: undefined, /* Argument Signature */
+          prorettypename: undefined, /* Return Type */
+          lanname: 'sql', /* Language Name in which function is being written */
+          prosrc: undefined,
+          proacl: undefined,
+          visibility: 'Unknown'
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: true
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'funcowner', label: gettext('Owner'), cell: 'string',
+          type: 'text', disabled: true
+        },{
+          id: 'pronargs', label: gettext('Argument count'), cell: 'string',
+          type: 'text', group: gettext('Definition'), mode: ['properties']
+        },{
+          id: 'proargs', label: gettext('Arguments'), cell: 'string',
+          type: 'text', group: gettext('Definition'), mode: ['properties'],
+          disabled: true
+        },{
+          id: 'proargtypenames', label: gettext('Signature arguments'), cell:
+          'string', type: 'text', group: gettext('Definition'), mode: ['properties'],
+          disabled: true
+        },{
+          id: 'prorettypename', label: gettext('Return type'), cell: 'string',
+          type: 'text', group: gettext('Definition'), disabled: true,
+          mode: ['properties'], visible: 'isVisible'
+        },{
+          id: 'visibility', label: gettext('Visibility'), cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: true
+        },{
+          id: 'lanname', label: gettext('Language'), cell: 'string',
+          type: 'text', group: gettext('Definition'), disabled: true
+        },{
+          id: 'prosrc', label: gettext('Code'), cell: 'string',
+          type: 'text', mode: ['properties'],
+          group: gettext('Definition'),
+          control: Backform.SqlFieldControl,
+          extraClasses:['custom_height_css_class'],
+          visible: function(m) {
+            if (m.get('lanname') == 'c') {
+              return false;
+            }
+            return true;
+          }, disabled: true
+        }],
+        validate: function()
+        {
+          return null;
+        },
+        isVisible: function(m){
+          if (this.name == 'sysproc') { return false; }
+          return true;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['edbfunc'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js
new file mode 100644
index 00000000..c13b9267
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js
@@ -0,0 +1,67 @@
+/* Create and Register Procedure Collection and Node. */
+define('pgadmin.node.edbproc', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.node.edbfunc', 'pgadmin.browser.collection',
+  'pgadmin.browser.server.privilege'
+], function(
+  gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify, EdbFunction
+) {
+
+  if (!pgBrowser.Nodes['coll-edbproc']) {
+    pgAdmin.Browser.Nodes['coll-edbproc'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'edbproc',
+        label: gettext('Procedures'),
+        type: 'coll-edbproc',
+        columns: ['name', 'funcowner', 'description'],
+        hasStatistics: true
+      });
+  };
+
+  // Inherit Functions Node
+  if (!pgBrowser.Nodes['edbproc']) {
+    pgAdmin.Browser.Nodes['edbproc'] = pgBrowser.Node.extend({
+      type: 'edbproc',
+      dialogHelp: url_for('help.static', {'filename': 'edbproc_dialog.html'}),
+      label: gettext('Procedure'),
+      collection_type: 'coll-edbproc',
+      hasDepends: true,
+      canEdit: false,
+      hasSQL: true,
+      hasScriptTypes: [],
+      parent_type: ['package'],
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.proc_initialized)
+            return;
+
+        this.proc_initialized = true;
+
+      },
+      canDrop: false,
+      canDropCascade: false,
+      model: EdbFunction.model.extend({
+        defaults: _.extend({},
+          EdbFunction.model.prototype.defaults,
+          {
+            lanname: 'edbspl'
+          }
+        ),
+        isVisible: function(m){
+          if (this.name == 'sysfunc') { return false; }
+          else if (this.name == 'sysproc') { return true; }
+          return false;
+        },
+        validate: function()
+        {
+          return null;
+        }
+      }
+      )
+  });
+
+  }
+
+  return pgBrowser.Nodes['edbproc'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py
index 96d78918..7a41194c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py
@@ -91,6 +91,14 @@ class EdbVarModule(CollectionNodeModule):
         """
         return False
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 blueprint = EdbVarModule(__name__)
 
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js
new file mode 100644
index 00000000..1933326d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js
@@ -0,0 +1,70 @@
+/* Create and Register Function Collection and Node. */
+define('pgadmin.node.edbvar', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.browser.collection', 'pgadmin.browser.server.privilege'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-edbvar']) {
+    pgBrowser.Nodes['coll-edbvar'] =
+      pgBrowser.Collection.extend({
+        node: 'edbvar',
+        label: gettext('Variables'),
+        type: 'coll-edbvar',
+        columns: ['name', 'funcowner', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['edbvar']) {
+    pgBrowser.Nodes['edbvar'] = pgBrowser.Node.extend({
+      type: 'edbvar',
+      dialogHelp: url_for('help.static', {'filename': 'edbvar_dialog.html'}),
+      label: gettext('Function'),
+      collection_type: 'coll-edbvar',
+      canEdit: false,
+      hasSQL: true,
+      hasScriptTypes: [],
+      parent_type: ['package'],
+      Init: function(args) {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+      },
+      canDrop: false,
+      canDropCascade: false,
+      model: pgBrowser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          datatype: undefined,
+          visibility: 'Unknown'
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: true
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'datatype', label: gettext('Data type'), cell: 'string',
+          type: 'text', disabled: true
+        },{
+          id: 'visibility', label: gettext('Visibility'), cell: 'string',
+          type: 'text', mode: ['properties'],
+          disabled: true
+        }],
+        validate: function()
+        {
+          return null;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['edbvar'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
index e22e54cf..b7bffbe8 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
@@ -9,6 +9,7 @@
 
 """ Implements Table Node """
 
+import os
 import simplejson as json
 import re
 
@@ -78,7 +79,8 @@ class TableModule(SchemaChildModule):
 
         scripts.append({
             'name': 'pgadmin.browser.table.partition.utils',
-            'path': url_for('browser.index') + 'table/static/js/partition.utils',
+            'path': os.path.join(self.root_path,
+                                 'static/js/partition.utils'),
             'when': 'database', 'is_template': False
         })
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
index b5b88e90..a22703c9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/__init__.py
@@ -92,6 +92,14 @@ class ColumnsModule(CollectionNodeModule):
         """
         return False
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = ColumnsModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
new file mode 100644
index 00000000..10737e0a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
@@ -0,0 +1,734 @@
+define('pgadmin.node.column', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'backform', 'alertify',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+  if (!pgBrowser.Nodes['coll-column']) {
+    var databases = pgBrowser.Nodes['coll-column'] =
+      pgBrowser.Collection.extend({
+        node: 'column',
+        label: gettext('Columns'),
+        type: 'coll-column',
+        columns: ['name', 'atttypid', 'description']
+      });
+  };
+
+   // This Node model will be used for variable control for column
+   var VariablesModel = Backform.VariablesModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      name: null,
+      value: null
+    },
+    schema: [{
+      id: 'name', label: gettext('Name'), cell: 'select2',
+      type: 'text', disabled: false, node: 'column',
+      options: [['n_distinct', 'n_distinct'],
+      ['n_distinct_inherited','n_distinct_inherited']],
+      select2: {placeholder: "Select variable"},
+      cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'value', label: gettext('Value'),
+      type: 'text', disabled: false,
+      cellHeaderClasses:'width_percent_50'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null;
+
+      if (_.isUndefined(this.get('value')) ||
+        _.isNull(this.get('value')) ||
+        String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+            errmsg = gettext('Please provide input for variable.');
+            this.errorModel.set('value', errmsg);
+            return errmsg;
+          } else {
+            this.errorModel.unset('value');
+          }
+      return null;
+    }
+  });
+
+  // Integer Cell for Columns Length and Precision
+  var IntegerDepCell = Backgrid.Extension.IntegerDepCell =
+    Backgrid.IntegerCell.extend({
+      initialize: function() {
+        Backgrid.NumberCell.prototype.initialize.apply(this, arguments);
+        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
+      },
+      dependentChanged: function () {
+        this.$el.empty();
+        var model = this.model,
+            column = this.column,
+            editable = this.column.get("editable"),
+            is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+
+        if (is_editable){ this.$el.addClass("editable"); }
+        else { this.$el.removeClass("editable"); }
+
+        this.delegateEvents();
+        return this;
+      },
+      render: function() {
+        Backgrid.NumberCell.prototype.render.apply(this, arguments);
+
+        var model = this.model,
+            column = this.column,
+            editable = this.column.get("editable"),
+            is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+
+        if (is_editable){ this.$el.addClass("editable"); }
+        else { this.$el.removeClass("editable"); }
+        return this;
+      },
+      remove: Backgrid.Extension.DependentCell.prototype.remove
+    });
+
+  if (!pgBrowser.Nodes['column']) {
+    pgBrowser.Nodes['column'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      parent_type: ['table', 'view', 'mview'],
+      collection_type: ['coll-table', 'coll-view', 'coll-mview'],
+      type: 'column',
+      label: gettext('Column'),
+      hasSQL:  true,
+      sqlAlterHelp: 'sql-altertable.html',
+      sqlCreateHelp: 'sql-altertable.html',
+      dialogHelp: url_for('help.static', {'filename': 'column_dialog.html'}),
+      canDrop: function(itemData, item, data){
+        if (pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data])) {
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+
+          // Check if menu is allowed ?
+          if(_.indexOf(parents, 'catalog') > -1 ||
+             _.indexOf(parents, 'view') > -1 ||
+             _.indexOf(parents, 'mview') > -1) {
+            return false;
+          } else if(_.indexOf(parents, 'table') > -1) {
+            return true;
+          }
+        } else {
+          return false;
+        }
+      },
+      hasDepends: true,
+      hasStatistics: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_column_on_coll', node: 'coll-column', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Column...'),
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column', node: 'column', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Column...'),
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Column...'),
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_column_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Column...'),
+          icon: 'wcTabIcon icon-column', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      model: pgBrowser.Node.Model.extend({
+        idAttribute: 'attnum',
+
+        defaults: {
+          name: undefined,
+          attowner: undefined,
+          atttypid: undefined,
+          attnum: undefined,
+          cltype: undefined,
+          collspcname: undefined,
+          attacl: undefined,
+          description: undefined,
+          parent_tbl: undefined,
+          min_val: undefined,
+          max_val: undefined,
+          edit_types: undefined,
+          is_primary_key: false,
+          inheritedfrom: undefined,
+          attstattarget:undefined,
+          attnotnull: false,
+          attlen: null,
+          attprecision: null
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', disabled: 'inSchemaWithColumnCheck',
+          cellHeaderClasses:'width_percent_30',
+          editable: 'editable_check_for_table'
+        },{
+          // Need to show this field only when creating new table
+          // [in SubNode control]
+          id: 'is_primary_key', label: gettext('Primary key?'),
+          cell: Backgrid.Extension.TableChildSwitchCell, type: 'switch',
+          deps:['name'], cellHeaderClasses:'width_percent_5',
+          options: {
+            onText: gettext('Yes'), offText: gettext('No'),
+            onColor: 'success', offColor: 'primary'
+          },
+          visible: function(m) {
+            return _.isUndefined(
+              m.top.node_info['table'] || m.top.node_info['view'] ||
+                m.top.node_info['mview']
+            );
+          },
+          disabled: function(m){
+            // Disable it, when one of this:
+            // - Primary key already exist
+            // - Table is a partitioned table
+            if (
+              m.top && ((
+                !_.isUndefined(m.top.get('oid')) &&
+                  m.top.get('primary_key').length > 0 &&
+                  !_.isUndefined(m.top.get('primary_key').first().get('oid'))
+              ) || (
+                m.top.has('is_partitioned') && m.top.get('is_partitioned')
+              ))
+            ) {
+              return true;
+            }
+
+            var name = m.get('name');
+
+            if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
+            (_.isUndefined(name)  || _.isNull(name) || name == '')) {
+              return true;
+            }
+            return false;
+          },
+          editable: function(m){
+            var name = m.get('name');
+            // If HeaderCell then allow True
+            if(m instanceof Backbone.Collection) {
+              return true;
+            }
+            // If primary key already exist then disable.
+            if (m.top && !_.isUndefined(m.top.get('oid')) &&
+                      m.top.get('primary_key').length > 0 &&
+                      !_.isUndefined(m.top.get('primary_key').first().get('oid'))) {
+
+              return false;
+            }
+
+            // If table is partitioned table then disable
+            if (m.top && !_.isUndefined(m.top.get('is_partitioned')) &&
+              m.top.get('is_partitioned'))
+            {
+              setTimeout(function () {
+                m.set('is_primary_key', false);
+              }, 10);
+
+              return false;
+            }
+
+            if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
+              !_.isUndefined(name) && !_.isNull(name) && name !== '') {
+              return true;
+            }
+            return false;
+            // Set to false if no condition is met
+            m.set('is_primary_key', false);
+          }
+        },{
+          id: 'attnum', label: gettext('Position'), cell: 'string',
+          type: 'text', disabled: 'notInSchema', mode: ['properties']
+        },{
+          id: 'cltype', label: gettext('Data type'),
+          cell: Backgrid.Extension.NodeAjaxOptionsCell.extend({
+            exitEditMode: function(e) {
+                var self = this;
+                this.$select.off('blur', this.exitEditMode);
+                this.$select.select2('close');
+                this.$el.removeClass('editor');
+                // Once user have selected a value
+                // we can shift to next cell if it is editable
+                var next_cell, length_cell = this.$el.next(),
+                  not_null_cell = this.$el.next().next().next();
+
+                // Add delay so that Select2 cell tab event is captured
+                // first before triggerring backgrid:edited event.
+                setTimeout(function() {
+                  // First check Length column if it is disable then goto
+                  // Not Null column
+                  if(length_cell && length_cell.hasClass('editable') && e) {
+                    next_cell = length_cell;
+                  } else if(not_null_cell && not_null_cell.hasClass('editable') && e) {
+                    next_cell = not_null_cell;
+                  }
+
+                  if(next_cell) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    var command = new Backgrid.Command({key: "Tab", keyCode: 9, which: 9});
+                    self.model.trigger("backgrid:edited", self.model, self.column,
+                                      command);
+                    next_cell.focus();
+                  }
+                }, 20);
+            }
+          }),
+          type: 'text', disabled: 'inSchemaWithColumnCheck',
+          control: 'node-ajax-options', url: 'get_types', node: 'table',
+          cellHeaderClasses:'width_percent_30', first_empty: true,
+          select2: { allowClear: false }, group: gettext('Definition'),
+          transform: function(data, cell) {
+            /* 'transform' function will be called by control, and cell both.
+             * The way, we use the transform in cell, and control is different.
+             * Because - options are shared using 'column' object in backgrid,
+             * hence - the cell is passed as second parameter, while the control
+             * uses (this) as a object.
+             */
+            var control = cell || this,
+                m = control.model;
+
+            /* We need different data in create mode & in edit mode
+             * if we are in create mode then return data as it is
+             * if we are in edit mode then we need to filter data
+             */
+            control.model.datatypes = data;
+            var edit_types = m.get('edit_types'),
+                result = [];
+
+            // If called from Table, We will check if in edit mode
+            // then send edit_types only
+            if( !_.isUndefined(m.top) && !m.top.isNew() ) {
+              _.each(data, function(t) {
+                if (_.indexOf(edit_types, t.value) != -1) {
+                  result.push(t);
+                }
+            });
+              // There may be case that user adds new column in  existing collection
+              // we will not have edit types then
+              return result.length > 0 ? result : data;
+            }
+
+            // If called from Column
+            if(m.isNew()) {
+              return data;
+            } else {
+              //edit mode
+              _.each(data, function(t) {
+                if (_.indexOf(edit_types, t.value) != -1) {
+                  result.push(t);
+                }
+              });
+
+              return result;
+            }
+          },
+          editable: 'editable_check_for_table'
+        },{
+          // Need to show this field only when creating new table [in SubNode control]
+          id: 'inheritedfrom', label: gettext('Inherited from table'),
+          type: 'text', disabled: true, editable: false,
+          cellHeaderClasses:'width_percent_10',
+          visible: function(m) {
+            return _.isUndefined(m.top.node_info['table'] || m.top.node_info['view'] || m.top.node_info['mview']);
+          }
+        },{
+          id: 'attlen', label: gettext('Length'), cell: IntegerDepCell,
+           deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses:'width_percent_20',
+           disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                    if(o.length)
+                    {
+                      m.set('min_val', o.min_val, {silent: true});
+                      m.set('max_val', o.max_val, {silent: true});
+                      flag = false;
+                    }
+                }
+              });
+
+              flag && setTimeout(function() {
+                  if(m.get('attlen')) {
+                    m.set('attlen', null);
+                  }
+                },10);
+
+              return flag;
+           },
+           editable: function(m) {
+             // inheritedfrom has value then we should disable it
+             if(!_.isUndefined(m.get('inheritedfrom'))) {
+                return false;
+             }
+
+             if (!m.datatypes) {
+              // datatypes not loaded, may be this call is from CallByNeed from backgrid cell initialize.
+              return true;
+             }
+             var of_type = m.get('cltype'),
+               flag = false;
+
+               _.each(m.datatypes, function(o) {
+               if ( of_type == o.value ) {
+                 if(o.length) {
+                   m.set('min_val', o.min_val, {silent: true});
+                   m.set('max_val', o.max_val, {silent: true});
+                   flag = true;
+                 }
+                }
+              });
+
+              !flag && setTimeout(function() {
+                  if(m.get('attlen')) {
+                    m.set('attlen', null, {silent: true});
+                  }
+                },10);
+
+              return flag;
+           }
+        },{
+          id: 'attprecision', label: gettext('Precision'), cell: IntegerDepCell,
+           deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses:'width_percent_20',
+           disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                  if(o.precision) {
+                    m.set('min_val', o.min_val, {silent: true});
+                    m.set('max_val', o.max_val, {silent: true});
+                    flag = false;
+                  }
+                }
+              });
+
+              flag && setTimeout(function() {
+                if(m.get('attprecision')) {
+                  m.set('attprecision', null);
+                }
+              },10);
+              return flag;
+           },
+           editable: function(m) {
+             // inheritedfrom has value then we should disable it
+             if(!_.isUndefined(m.get('inheritedfrom'))) {
+                return false;
+             }
+
+             if (!m.datatypes) {
+              // datatypes not loaded yet, may be this call is from CallByNeed from backgrid cell initialize.
+              return true;
+             }
+
+             var of_type = m.get('cltype'),
+               flag = false;
+              _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                  if(o.precision) {
+                    m.set('min_val', o.min_val, {silent: true});
+                    m.set('max_val', o.max_val, {silent: true});
+                    flag = true;
+                  }
+                }
+              });
+
+              !flag && setTimeout(function() {
+                if(m.get('attprecision')) {
+                  m.set('attprecision', null);
+                }
+              },10);
+
+              return flag;
+           }
+         },{
+          id: 'collspcname', label: gettext('Collation'), cell: 'string',
+          type: 'text', control: 'node-ajax-options', url: 'get_collations',
+          group: gettext('Definition'), node: 'collation',
+          deps: ['cltype'], disabled: function(m) {
+             var of_type = m.get('cltype'),
+               flag = true;
+             _.each(m.datatypes, function(o) {
+                if ( of_type == o.value ) {
+                    if(o.is_collatable)
+                    {
+                      flag = false;
+                    }
+                }
+             });
+             if (flag) {
+                setTimeout(function(){
+                  if(m.get('collspcname') && m.get('collspcname') !== '') {
+                    m.set('collspcname', "");
+                  }
+                }, 10);
+             }
+             return flag;
+          }
+        },{
+          id: 'defval', label: gettext('Default Value'), cell: 'string',
+          type: 'text', group: gettext('Definition'), deps: ['cltype'],
+          disabled: function(m) {
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+              var type = m.get('cltype');
+                return type == 'serial' || type == 'bigserial'
+                                        || type == 'smallserial';
+            }
+          }
+        },{
+          id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
+          type: 'switch', disabled: 'inSchemaWithColumnCheck', cellHeaderClasses:'width_percent_20',
+          group: gettext('Definition'), editable: 'editable_check_for_table',
+          options: { onText: 'Yes', offText: 'No', onColor: 'success', offColor: 'primary' }
+        },{
+          id: 'attstattarget', label: gettext('Statistics'), cell: 'string',
+          type: 'text', disabled: 'inSchemaWithColumnCheck', mode: ['properties', 'edit'],
+          group: gettext('Definition')
+        },{
+          id: 'attstorage', label: gettext('Storage'), group: gettext('Definition'),
+          type: 'text', mode: ['properties', 'edit'],
+          cell: 'string', disabled: 'inSchemaWithColumnCheck', first_empty: true,
+          control: 'select2', select2: { placeholder: "Select storage",
+            allowClear: false,
+            width: "100%"
+          },
+          options: [
+            {label: "PLAIN", value: "p"},
+            {label: "MAIN", value: "m"},
+            {label: "EXTERNAL", value: "e"},
+            {label: "EXTENDED", value: "x"},
+           ]
+        },{
+          id: 'is_pk', label: gettext('Primary key?'),
+          type: 'switch', disabled: true, mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'is_fk', label: gettext('Foreign key?'),
+          type: 'switch', disabled: true, mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'is_inherited', label: gettext('Inherited?'),
+          type: 'switch', disabled: true, mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'tbls_inherited', label: gettext('Inherited from table(s)'),
+          type: 'text', disabled: true, mode: ['properties'], deps: ['is_inherited'],
+          group: gettext('Definition'),
+          visible: function(m) {
+              if (!_.isUndefined(m.get('is_inherited')) && m.get('is_inherited')) {
+                return true;
+              } else {
+                return false;
+              }
+          }
+        },{
+          id: 'is_sys_column', label: gettext('System column?'), cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'notInSchema'
+        },{
+          id: 'attoptions', label: 'Variables', type: 'collection',
+          group: gettext('Variables'), control: 'unique-col-collection',
+          model: VariablesModel, uniqueCol : ['name'],
+          mode: ['edit', 'create'], canAdd: true, canEdit: false,
+          canDelete: true
+        }, pgBrowser.SecurityGroupUnderSchema, {
+          id: 'attacl', label: 'Privileges', type: 'collection',
+          group: 'security', control: 'unique-col-collection',
+          model: pgBrowser.Node.PrivilegeRoleModel.extend({
+          privileges: ['a','r','w','x']}),
+          mode: ['edit'], canAdd: true, canDelete: true,
+          uniqueCol : ['grantee']
+        },{
+          id: 'seclabels', label: gettext('Security Labels'), canAdd: true,
+          model: pgBrowser.SecLabelModel, group: 'security',
+          mode: ['edit', 'create'], editable: false, type: 'collection',
+          min_version: 90100, canEdit: false, canDelete: true,
+          control: 'unique-col-collection'
+        }],
+        validate: function(keys) {
+          var err = {},
+              changedAttrs = this.changed,
+              msg = undefined;
+
+          // Nothing to validate
+          if (keys && keys.length == 0) {
+            this.errorModel.clear();
+            return null;
+          } else {
+            this.errorModel.clear();
+          }
+
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Column name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('cltype'))
+              || String(this.get('cltype')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Column type cannot be empty.');
+            this.errorModel.set('cltype', msg);
+            return msg;
+          }
+
+          if (!_.isUndefined(this.get('cltype'))
+                && !_.isUndefined(this.get('attlen'))
+                && !_.isNull(this.get('attlen'))
+                && this.get('attlen') !== '') {
+            // Validation for Length field
+            if (this.get('attlen') < this.get('min_val'))
+              msg = gettext('Length should not be less than: ') + this.get('min_val');
+            if (this.get('attlen') > this.get('max_val'))
+              msg = gettext('Length should not be greater than: ') + this.get('max_val');
+            // If we have any error set then throw it to user
+            if(msg) {
+              this.errorModel.set('attlen', msg)
+              return msg;
+            }
+          }
+
+          if (!_.isUndefined(this.get('cltype'))
+                && !_.isUndefined(this.get('attprecision'))
+                && !_.isNull(this.get('attprecision'))
+                && this.get('attprecision') !== '') {
+            // Validation for precision field
+            if (this.get('attprecision') < this.get('min_val'))
+              msg = gettext('Precision should not be less than: ') + this.get('min_val');
+            if (this.get('attprecision') > this.get('max_val'))
+              msg = gettext('Precision should not be greater than: ') + this.get('max_val');
+            // If we have any error set then throw it to user
+            if(msg) {
+              this.errorModel.set('attprecision', msg)
+              return msg;
+            }
+          }
+
+          return null;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        notInSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info)
+          {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info)
+          {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          var node_info = this.node_info || m.node_info || m.top.node_info;
+
+          // disable all fields if column is listed under view or mview
+          if (node_info && ('view' in node_info || 'mview' in node_info)) {
+            if (this && _.has(this, 'name') && (this.name != 'defval')) {
+              return true;
+            }
+          }
+
+          if(node_info &&  'schema' in node_info)
+          {
+            // We will disable control if it's system columns
+            // inheritedfrom check is useful when we use this schema in table node
+            // inheritedfrom has value then we should disable it
+            if(!_.isUndefined(m.get('inheritedfrom'))) {
+                return true;
+            }
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            }
+            // if we are in edit mode
+            if (!_.isUndefined(m.get('attnum')) && m.get('attnum') > 0 ) {
+              return false;
+            } else {
+              return true;
+            }
+           }
+          return true;
+        },
+        editable_check_for_table: function(arg) {
+            if (arg instanceof Backbone.Collection) {
+                return !arg.model.prototype.inSchemaWithColumnCheck.apply(
+                         this, [arg.top]
+                       );
+            } else {
+                return !arg.inSchemaWithColumnCheck.apply(
+                         this, [arg]
+                       );
+            }
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to create table
+            if (_.indexOf(['schema'], d._type) > -1) {
+              return true;
+            }
+            else if (_.indexOf(['view', 'coll-view',
+                                'mview',
+                                'coll-mview'], d._type) > -1) {
+              parents.push(d._type);
+              break;
+            }
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1 ||
+              _.indexOf(parents, 'coll-view') > -1 ||
+              _.indexOf(parents, 'coll-mview') > -1 ||
+              _.indexOf(parents, 'mview') > -1 ||
+              _.indexOf(parents, 'view') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+    });
+  }
+
+  return pgBrowser.Nodes['column'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py
index 4fda9d76..88cbe1d6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/__init__.py
@@ -72,6 +72,14 @@ class ConstraintsModule(CollectionNodeModule):
         """
         return database.DatabaseModule.NODE_TYPE
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = ConstraintsModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py
index e3022aba..9de9d41e 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/__init__.py
@@ -46,7 +46,7 @@ class CheckConstraintModule(CollectionNodeModule):
       - Load the module script for the Check Constraint, when any of the
         Check node is initialized.
     """
-    NODE_TYPE = 'check_constraints'
+    NODE_TYPE = 'check_constraint'
     COLLECTION_LABEL = _("Check Constraints")
 
     def __init__(self, *args, **kwargs):
@@ -75,6 +75,16 @@ class CheckConstraintModule(CollectionNodeModule):
         """
         return database.DatabaseModule.NODE_TYPE
 
+
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
+
     @property
     def csssnippets(self):
         """
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
new file mode 100644
index 00000000..52a197ea
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
@@ -0,0 +1,235 @@
+// Check Constraint Module: Node
+define('pgadmin.node.check_constraint', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'sources/alerts/alertify_wrapper',
+
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, AlertifyWrapper) {
+
+  // Check Constraint Node
+  if (!pgBrowser.Nodes['check_constraints']) {
+    pgAdmin.Browser.Nodes['check_constraints'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      type: 'check_constraints',
+      label: gettext('Check'),
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      dialogHelp: url_for('help.static', {'filename': 'check_dialog.html'}),
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['table','partition'],
+      Init: function() {
+        // Avoid mulitple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_check_constraints_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: gettext('Check...'),
+          icon: 'wcTabIcon icon-check_constraints', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'validate_check_constraint', node: 'check_constraints', module: this,
+          applies: ['object', 'context'], callback: 'validate_check_constraint',
+          category: 'validate', priority: 4, label: gettext('Validate check constraint'),
+          icon: 'fa fa-link', enable : 'is_not_valid', data: {action: 'edit', check: true}
+        }
+        ]);
+
+      },
+      is_not_valid: function(itemData, item, data) {
+        if (this.canCreate(itemData, item, data)) {
+          return (itemData && !itemData.valid);
+        } else {
+          return false;
+        }
+      },
+      callbacks: {
+        validate_check_constraint: function(args) {
+          var input = args || {},
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d) {
+            return false;
+          }
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'validate', d, true),
+            type:'GET',
+            success: function(res) {
+              if (res.success == 1) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(res.info);
+                t.removeIcon(i);
+                data.valid = true;
+                data.icon = 'icon-check_constraints';
+                t.addIcon(i, {icon: data.icon});
+                setTimeout(function() {t.deselect(i);}, 10);
+                setTimeout(function() {t.select(i);}, 100);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(err.errormsg);
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+            });
+
+          return false;
+        }
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'oid',
+
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          description: undefined,
+          consrc: undefined,
+          connoinherit: undefined,
+          convalidated: true
+        },
+        // Check Constraint Schema
+        schema: [{
+          id: 'name', label: gettext('Name'), type:'text', cell:'string',
+          disabled: 'isDisabled'
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label: gettext('Comment'), type: 'multiline', cell:
+          'string', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                if(m.get('comment') && m.get('comment') !== '')
+                  m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'consrc', label: gettext('Check'), type: 'multiline', cell:
+          'string', group: gettext('Definition'), mode: ['properties',
+          'create', 'edit'], disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }, editable: false
+        },{
+          id: 'connoinherit', label: gettext('No Inherit?'), type:
+          'switch', cell: 'boolean', group: gettext('Definition'), mode:
+          ['properties', 'create', 'edit'], min_version: 90200,
+          disabled: function(m) {
+            // Disabled if table is a partitioned table.
+            if ((_.has(m , 'top') && !_.isUndefined(m.top) && m.top.get('is_partitioned')) ||
+                (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
+                _.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
+            ){
+              setTimeout(function(){
+                  m.set('connoinherit', false);
+              },10);
+
+              return true;
+            }
+
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'convalidated', label: gettext("Don't validate?"), type: 'switch', cell:
+          'boolean', group: gettext('Definition'), min_version: 90200,
+          disabled: function(m) {
+            if ((_.isFunction(m.isNew) && !m.isNew()) ||
+                  (_.has(m, 'handler') &&
+                  !_.isUndefined(m.handler) &&
+                  !_.isUndefined(m.get('oid')))) {
+
+              return !m.get("convalidated");
+            } else {
+              return false;
+            }
+          },
+          mode: ['properties', 'create', 'edit']
+        }],
+        // Client Side Validation
+        validate: function() {
+          var err = {},
+              errmsg;
+
+          if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') {
+            err['consrc'] = gettext('Check cannot be empty!');
+            errmsg = errmsg || err['consrc'];
+          }
+
+          this.errorModel.clear().set(err);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          return null;
+
+        },
+        isDisabled: function(m){
+          if ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) ||
+            (_.isFunction(m.isNew) && !m.isNew())) {
+            var server = (this.node_info || m.top.node_info).server;
+            if (server.version < 90200)
+            {
+              return true;
+            }
+          }
+          return false;
+        }
+      }),
+      // Below function will enable right click menu for creating check constraint.
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['check_constraints'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_add.py
index 1e28d51c..13a12a88 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_add.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_add.py
@@ -25,7 +25,7 @@ class CheckConstraintAddTestCase(BaseTestGenerator):
     """This class will add check constraint to existing table"""
     scenarios = [
         ('Add check constraint to table',
-         dict(url='/browser/check_constraints/obj/'))
+         dict(url='/browser/check_constraint/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_delete.py
index 0dd361dc..ae58c9bb 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_delete.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_delete.py
@@ -25,7 +25,7 @@ class CheckConstraintDeleteTestCase(BaseTestGenerator):
     """This class will delete check constraint to existing table"""
     scenarios = [
         ('Delete check constraint to table',
-         dict(url='/browser/check_constraints/obj/'))
+         dict(url='/browser/check_constraint/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_get.py
index ee94bbc4..971570ae 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_get.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_get.py
@@ -25,7 +25,7 @@ class CheckConstraintGetTestCase(BaseTestGenerator):
     """This class will fetch check constraint to existing table"""
     scenarios = [
         ('Fetch check constraint to table',
-         dict(url='/browser/check_constraints/obj/'))
+         dict(url='/browser/check_constraint/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_put.py
index 6192ae5a..e016dc60 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_put.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/tests/test_check_constraint_put.py
@@ -26,7 +26,7 @@ class CheckConstraintPutTestCase(BaseTestGenerator):
     """This class will update check constraint to existing table"""
     scenarios = [
         ('Update check constraint to table',
-         dict(url='/browser/check_constraints/obj/'))
+         dict(url='/browser/check_constraint/obj/'))
     ]
 
     def setUp(self):
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
index b8685030..24ec064b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
@@ -93,6 +93,14 @@ class ExclusionConstraintModule(ConstraintTypeModule):
         """
         return database.DatabaseModule.NODE_TYPE
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = ExclusionConstraintModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
new file mode 100644
index 00000000..065b6096
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
@@ -0,0 +1,951 @@
+define('pgadmin.node.exclusion_constraint', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify) {
+
+  var ExclusionConstraintColumnModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      column: undefined,
+      oper_class: undefined,
+      order: false,
+      nulls_order: false,
+      operator:undefined,
+      col_type:undefined,
+      is_sort_nulls_applicable: true
+    },
+    toJSON: function () {
+      var d = pgBrowser.Node.Model.prototype.toJSON.apply(this, arguments);
+      delete d.col_type;
+      return d;
+    },
+    schema: [{
+        id: 'column', label: gettext('Column'), type:'text', editable: false,
+        cell:'string'
+      },{
+        id: 'oper_class', label: gettext('Operator class'), type:'text',
+        node: 'table', url: 'get_oper_class', first_empty: true,
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          } else if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          } else if (_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('amname')) &&
+                m.collection.handler.get('amname') != 'btree') {
+            // Disable if access method is not btree
+            return false;
+          }
+          return true;
+        },
+        select2: {
+          allowClear: true, width: 'style', tags: true,
+          placeholder: gettext('Select the operator class')
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+          initialize: function () {
+            Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            indextype = self.model.collection.handler.get('amname');
+
+            if (url && (indextype == 'btree' || _.isUndefined(indextype) ||
+                _.isNull(indextype) || indextype == '')) {
+              // Set sort_order and nulls to true if access method is btree
+              setTimeout(function() {
+                m.set('order', true);
+                m.set('nulls_order', true);
+              }, 10);
+
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              indextype = 'btree';
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {indextype:indextype},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                    self.column.set('options', data);
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+            } else {
+              self.column.set('options', []);
+            }
+          }
+        })
+      },{
+        id: 'order', label: gettext('DESC'), type: 'switch',
+        options: {
+          onText: 'ASC',
+          offText: 'DESC',
+        },
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          } else if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          } else if (m.top.get('amname') === 'btree') {
+            m.set('is_sort_nulls_applicable', true);
+            return true;
+          } else {
+            m.set('is_sort_nulls_applicable', false);
+            return false;
+          }
+        }
+      },{
+        id: 'nulls_order', label: gettext('NULLs order'), type:"switch",
+        options: {
+          onText: 'FIRST',
+          offText: 'LAST',
+        },
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          } else if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          } else if (m.top.get('amname') === 'btree') {
+              m.set('is_sort_nulls_applicable', true);
+              return true;
+          } else {
+            m.set('is_sort_nulls_applicable', false);
+            return false;
+          }
+        }
+      },{
+        id: 'operator', label: gettext('Operator'), type: 'text',
+        node: 'table', url: 'get_operator',
+        editable: function(m) {
+          if (m instanceof Backbone.Collection) {
+            return true;
+          }
+          if ((_.has(m.collection, 'handler') &&
+                !_.isUndefined(m.collection.handler) &&
+                !_.isUndefined(m.collection.handler.get('oid')))) {
+            return false;
+          }
+          return true;
+        },
+        select2: {
+          allowClear: false, width: 'style',
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+        initialize: function () {
+          Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            col_type = self.model.get('col_type');
+
+            self.column.set('options', []);
+
+            if (url && !_.isUndefined(col_type) && !_.isNull(col_type) && col_type != '') {
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {col_type:col_type},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                    self.column.set('options', data);
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+            }
+          }
+        })
+      }
+    ],
+    validate: function() {
+      this.errorModel.clear();
+      var operator = this.get('operator'),
+        column_name = this.get('column');
+      if (_.isUndefined(operator) || _.isNull(operator)) {
+        var msg = gettext('Please specify operator for column: ') + column_name;
+        this.errorModel.set('operator', msg);
+        return msg;
+      }
+      return null;
+    }
+  });
+
+  var ExclusionConstraintColumnControl =  Backform.ExclusionConstraintColumnControl =
+    Backform.UniqueColCollectionControl.extend({
+
+    initialize: function(opts) {
+      Backform.UniqueColCollectionControl.prototype.initialize.apply(
+        this, arguments
+          );
+
+      var self = this,
+        node = 'exclusion_constraint',
+        headerSchema = [{
+          id: 'column', label:'', type:'text',
+          node: 'column', control: Backform.NodeListByNameControl.extend({
+            initialize: function() {
+              // Here we will decide if we need to call URL
+              // Or fetch the data from parent columns collection
+                if(self.model.handler) {
+                  Backform.Select2Control.prototype.initialize.apply(this, arguments);
+                  // Do not listen for any event(s) for existing constraint.
+                  if (_.isUndefined(self.model.get('oid'))) {
+                    var tableCols = self.model.top.get('columns');
+                    this.listenTo(tableCols, 'remove' , this.removeColumn);
+                    this.listenTo(tableCols, 'change:name', this.resetColOptions);
+                    this.listenTo(tableCols, 'change:cltype', this.resetColOptions);
+                  }
+                  this.custom_options();
+                } else {
+                  Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+                }
+            },
+            removeColumn: function (m) {
+              var that = this;
+              setTimeout(function   () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            resetColOptions: function(m) {
+              var that = this;
+
+              if (m.previous('name') ==  self.headerData.get('column')) {
+                /*
+                 * Table column name has changed so update
+                 * column name in exclusion constraint as well.
+                 */
+                self.headerData.set(
+                  {"column": m.get('name')});
+                  self.headerDataChanged();
+              }
+
+              setTimeout(function () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            custom_options: function() {
+              // We will add all the columns entered by user in table model
+              var columns = self.model.top.get('columns'),
+                  added_columns_from_tables = [],
+                  col_types = [];
+
+              if (columns.length > 0) {
+                _.each(columns.models, function(m) {
+                  var col = m.get('name');
+                  if(!_.isUndefined(col) && !_.isNull(col)) {
+                    added_columns_from_tables.push({
+                      label: col, value: col, image:'icon-column'
+                      });
+                    col_types.push({name:col, type:m.get('cltype')});
+                  }
+                });
+              }
+              // Set the values in to options so that user can select
+              this.field.set('options', added_columns_from_tables);
+              self.field.set('col_types', col_types);
+            },
+            remove: function () {
+              if(self.model.handler) {
+                  var tableCols = self.model.top.get('columns');
+                  this.stopListening(tableCols, 'remove' , this.removeColumn);
+                  this.stopListening(tableCols, 'change:name' , this.resetColOptions);
+                  this.stopListening(tableCols, 'change:cltype' , this.resetColOptions);
+
+                Backform.Select2Control.prototype.remove.apply(this, arguments);
+
+              } else {
+                Backform.NodeListByNameControl.prototype.remove.apply(this, arguments);
+              }
+            },
+            template: _.template([
+              '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+              '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+              '    <% for (var i=0; i < options.length; i++) { %>',
+              '    <% var option = options[i]; %>',
+              '    <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> value=<%= formatter.fromRaw(option.value) %> <%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>',
+              '    <% } %>',
+              '  </select>',
+              '</div>'].join("\n"))
+          }),
+          transform: function(rows) {
+            // This will only get called in case of NodeListByNameControl.
+
+            var that = this,
+                node = that.field.get('schema_node'),
+                res = [],
+                col_types = [],
+                filter = that.field.get('filter') || function() { return true; };
+
+            filter = filter.bind(that);
+
+            _.each(rows, function(r) {
+              if (filter(r)) {
+                var l = (_.isFunction(node['node_label']) ?
+                      (node['node_label']).apply(node, [r, that.model, that]) :
+                      r.label),
+                    image = (_.isFunction(node['node_image']) ?
+                      (node['node_image']).apply(
+                        node, [r, that.model, that]
+                        ) :
+                      (node['node_image'] || ('icon-' + node.type)));
+                res.push({
+                  'value': r.label,
+                  'image': image,
+                  'label': l
+                });
+                col_types.push({name:r.label, type:r.datatype});
+              }
+            });
+            self.field.set('col_types', col_types);
+            return res;
+          },
+          canAdd: function(m) {
+            return !((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select column'
+          }, first_empty: !self.model.isNew(),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        }],
+        headerDefaults = {column: null},
+
+        gridCols = ['column', 'oper_class', 'order', 'nulls_order', 'operator'];
+
+      self.headerData = new (Backbone.Model.extend({
+        defaults: headerDefaults,
+        schema: headerSchema
+      }))({});
+
+      var headerGroups = Backform.generateViewSchema(
+          self.field.get('node_info'), self.headerData, 'create',
+          node, self.field.get('node_data')
+          ),
+          fields = [];
+
+      _.each(headerGroups, function(o) {
+        fields = fields.concat(o.fields);
+      });
+
+      self.headerFields = new Backform.Fields(fields);
+      self.gridSchema = Backform.generateGridColumnsFromModel(
+          self.field.get('node_info'), self.field.get('model'), 'edit', gridCols, self.field.get('schema_node')
+          );
+
+      self.controls = [];
+      self.listenTo(self.headerData, "change", self.headerDataChanged);
+      self.listenTo(self.headerData, "select2", self.headerDataChanged);
+      self.listenTo(self.collection, "add", self.onAddorRemoveColumns);
+      self.listenTo(self.collection, "remove", self.onAddorRemoveColumns);
+    },
+
+    generateHeader: function(data) {
+      var header = [
+        '<div class="subnode-header-form">',
+        ' <div class="container-fluid">',
+        '  <div class="row">',
+        '   <div class="col-xs-4">',
+        '    <label class="control-label"><%-column_label%></label>',
+        '   </div>',
+        '   <div class="col-xs-4" header="column"></div>',
+        '   <div class="col-xs-4">',
+        '     <button class="btn-sm btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
+        '   </div>',
+        '  </div>',
+        ' </div>',
+        '</div>',].join("\n")
+
+      _.extend(data, {
+        column_label: gettext('Column')
+      });
+
+      var self = this,
+          headerTmpl = _.template(header),
+          $header = $(headerTmpl(data)),
+          controls = this.controls;
+
+      this.headerFields.each(function(field) {
+        var control = new (field.get("control"))({
+          field: field,
+          model: self.headerData
+        });
+
+        $header.find('div[header="' + field.get('name') + '"]').append(
+          control.render().$el
+        );
+
+        controls.push(control);
+      });
+
+      // We should not show in properties mode
+      if (data.mode == 'properties') {
+        $header.html('');
+      }
+
+      self.$header = $header;
+
+      return $header;
+    },
+
+    events: _.extend(
+                  {}, Backform.UniqueColCollectionControl.prototype.events,
+                {'click button.add': 'addColumns'}
+                ),
+
+    showGridControl: function(data) {
+      var self = this,
+          titleTmpl = _.template("<div class='subnode-header'></div>"),
+          $gridBody =
+            $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
+              // Append titleTmpl only if create/edit mode
+              data.mode !== 'properties' ? titleTmpl({label: data.label}) : ''
+            );
+
+      $gridBody.append(self.generateHeader(data));
+
+      var gridColumns = _.clone(this.gridSchema.columns);
+
+      // Insert Delete Cell into Grid
+      if (data.disabled == false && data.canDelete) {
+          gridColumns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: Backgrid.Extension.DeleteCell,
+            editable: false, cell_priority: -1
+          });
+      }
+
+      if (self.grid) {
+        self.grid.remove();
+        self.grid.null;
+      }
+      // Initialize a new Grid instance
+      var grid = self.grid = new Backgrid.Grid({
+        columns: gridColumns,
+        collection: self.collection,
+        className: "backgrid table-bordered"
+      });
+      self.$grid = grid.render().$el;
+
+      $gridBody.append(self.$grid);
+
+      setTimeout(function() {
+        self.headerData.set({
+          'column': self.$header.find(
+            'div[header="column"] select'
+            ).val()
+            }, {silent:true}
+          );
+      }, 10);
+
+      // Remove unwanted class from grid to display it properly
+      if(data.mode === 'properties')
+        $gridBody.find('.subnode-header-form').removeClass('subnode-header-form');
+
+      // Render node grid
+      return $gridBody;
+    },
+
+    headerDataChanged: function() {
+      var self = this, val,
+          data = this.headerData.toJSON(),
+          inSelected = false,
+          checkVars = ['column'];
+
+      if (!self.$header) {
+        return;
+      }
+
+      if (self.control_data.canAdd) {
+        self.collection.each(function(m) {
+          if (!inSelected) {
+            _.each(checkVars, function(v) {
+              if (!inSelected) {
+                val = m.get(v);
+                inSelected = ((
+                  (_.isUndefined(val) || _.isNull(val)) &&
+                  (_.isUndefined(data[v]) || _.isNull(data[v]))
+                  ) ||
+                  (val == data[v]));
+              }
+            });
+          }
+        });
+      }
+      else {
+        inSelected = true;
+      }
+
+      self.$header.find('button.add').prop('disabled', inSelected);
+    },
+
+    addColumns: function(ev) {
+      ev.preventDefault();
+      var self = this,
+          column = self.headerData.get('column');
+
+      if (!column || column == '') {
+        return false;
+      }
+
+      var coll = self.model.get(self.field.get('name')),
+          m = new (self.field.get('model'))(
+                self.headerData.toJSON(), {
+                  silent: true, top: self.model.top,
+                  collection: coll, handler: coll
+                }),
+          col_types =self.field.get('col_types') || [];
+
+      for(var i=0; i < col_types.length; i++) {
+        var col_type = col_types[i];
+        if (col_type['name'] ==  m.get('column')) {
+            m.set({'col_type':col_type['type']});
+          break;
+        }
+      }
+
+      coll.add(m);
+
+      var idx = coll.indexOf(m);
+
+      // idx may not be always > -1 because our UniqueColCollection may
+      // remove 'm' if duplicate value found.
+      if (idx > -1) {
+        self.$grid.find('.new').removeClass('new');
+
+        var newRow = self.grid.body.rows[idx].$el;
+
+        newRow.addClass("new");
+        $(newRow).pgMakeVisible('backform-tab');
+      } else {
+        //delete m;
+      }
+
+      return false;
+    },
+
+    onAddorRemoveColumns: function() {
+      var self = this;
+
+      // Wait for collection to be updated before checking for the button to be
+      // enabled, or not.
+      setTimeout(function() {
+          self.collection.trigger('pgadmin:columns:updated', self.collection);
+        self.headerDataChanged();
+      }, 10);
+    },
+
+    remove: function() {
+      /*
+       * Stop listening the events registered by this control.
+       */
+      this.stopListening(this.headerData, "change", this.headerDataChanged);
+      this.listenTo(this.headerData, "select2", this.headerDataChanged);
+      this.listenTo(this.collection, "remove", this.onAddorRemoveColumns);
+
+      // Remove header controls.
+      _.each(this.controls, function(controls) {
+        controls.remove();
+      });
+
+      ExclusionConstraintColumnControl.__super__.remove.apply(this, arguments);
+
+      // Remove the header model
+      delete (this.headerData);
+
+    }
+  });
+
+  // Extend the browser's node class for exclusion constraint node
+  if (!pgBrowser.Nodes['exclusion_constraint']) {
+    pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      type: 'exclusion_constraint',
+      label: gettext('Exclusion constraint'),
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      dialogHelp: url_for('help.static', {'filename': 'exclusion_constraint_dialog.html'}),
+      hasSQL: true,
+      parent_type: ['table','partition'],
+      canDrop: true,
+      canDropCascade: true,
+      hasDepends: true,
+      hasStatistics: true,
+      statsPrettifyFields: ['Index size'],
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_exclusion_constraint_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Exclusion constraint...'),
+          icon: 'wcTabIcon icon-exclusion_constraint', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }]);
+      },
+      is_not_valid: function(node) {
+        return (node && !node.valid);
+      },
+      // Define the model for exclusion constraint node
+      model: pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'oid',
+
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          spcname: undefined,
+          amname: "gist",
+          fillfactor: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          columns: []
+        },
+
+        // Define the schema for the exclusion constraint node
+        schema: [{
+          id: 'name', label: gettext('Name'), type: 'text',
+          mode: ['properties', 'create', 'edit'], editable: true,
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+              if(m.get('comment') && m.get('comment') !== '')
+                 m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'spcname', label: gettext('Tablespace'),
+          type: 'text', group: gettext('Definition'),
+          control: 'node-list-by-name', node: 'tablespace',
+          select2:{allowClear:false},
+          filter: function(m) {
+            // Don't show pg_global tablespace in selection.
+            if (m.label == "pg_global") return false;
+            else return true;
+          }
+        },{
+          id: 'amname', label: gettext('Access method'),
+          type: 'text', group: gettext('Definition'),
+          url:"get_access_methods", node: 'table',
+          control: Backform.NodeAjaxOptionsControl.extend({
+            // When access method changes we need to clear columns collection
+            onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              var self = this,
+              // current access method
+              current_am = self.model.get('amname'),
+              // previous access method
+              previous_am = self.model.previous('amname'),
+              column_collection = self.model.get('columns');
+
+              if (column_collection.length > 0 && current_am != previous_am) {
+                var msg = gettext('Changing access method will clear columns collection');
+                Alertify.confirm(msg, function (e) {
+                    // User clicks Ok, lets clear collection.
+                    column_collection.each(function(m) {
+                      /*
+                       * Our datamodel do not support collection reset method.
+                       * So remove model one by one.
+                       */
+                      column_collection.remove(m);
+                    });
+                    setTimeout(function() {
+                      column_collection.trigger('pgadmin:columns:updated', column_collection);
+                    }, 10);
+
+                  }, function() {
+                    // User clicks Cancel set previous value again in combo box
+                    setTimeout(function(){
+                      self.model.set('amname', previous_am);
+                    }, 10);
+                });
+              }
+            }
+          }),
+          select2:{allowClear:true},
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'fillfactor', label: gettext('Fill factor'),
+          type: 'int', group: gettext('Definition'), allowNull: true
+        },{
+          id: 'condeferrable', label: gettext('Deferrable?'),
+          type: 'switch', group: gettext('Definition'), deps: ['index'],
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'condeferred', label: gettext('Deferred?'),
+          type: 'switch', group: gettext('Definition'),
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            if((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew())) {
+              return true;
+            }
+
+            // Disable if condeferred is false or unselected.
+            if(m.get('condeferrable') == true) {
+              return false;
+            } else {
+              setTimeout(function(){
+                if(m.get('condeferred'))
+                  m.set('condeferred', false);
+              },10);
+              return true;
+            }
+          }
+        },{
+          id: 'constraint', label: gettext('Constraint'), cell: 'string',
+          type: 'multiline', mode: ['create', 'edit'], editable: false,
+          group: gettext('Definition'), disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          }
+        },{
+          id: 'columns', label: gettext('Columns'),
+          type: 'collection', group: gettext('Columns'),
+          deps:['amname'], canDelete: true, editable: false,
+          canAdd: function(m) {
+            // We can't update columns of existing exclusion constraint.
+            return !((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          control: ExclusionConstraintColumnControl,
+          model: ExclusionConstraintColumnModel,
+          disabled: function(m) {
+            return ((_.has(m, 'handler') &&
+              !_.isUndefined(m.handler) &&
+              !_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
+          },
+          cell: Backgrid.StringCell.extend({
+            initialize: function() {
+              Backgrid.StringCell.prototype.initialize.apply(this, arguments);
+              var self = this;
+              // Do not listen for any event(s) for existing constraint.
+              if (_.isUndefined(self.model.get('oid'))) {
+                var tableCols = self.model.top.get('columns');
+
+                self.listenTo(tableCols, 'remove' , self.removeColumn);
+                self.listenTo(tableCols, 'change:name', self.resetColOptions);
+                self.listenTo(tableCols, 'change:cltype', self.removeColumnWithType);
+              }
+
+              this.model.get('columns').on('pgadmin:columns:updated', function() {
+                self.render.apply(self);
+              });
+            },
+            removeColumnWithType: function(m){
+              var self = this,
+                  cols = self.model.get('columns'),
+                  removedCols = cols.where(
+                    {col_type: m.previous('cltype')}
+                    );
+
+              cols.remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                var constraints = self.model.top.get("exclude_constraint");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            removeColumn: function(m){
+              var self = this,
+                  removedCols = self.model.get('columns').where(
+                    {column: m.get('name')}
+                    );
+
+              self.model.get('columns').remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                var constraints = self.model.top.get("exclude_constraint");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            resetColOptions : function(m) {
+                var self = this,
+                  updatedCols = self.model.get('columns').where(
+                  {"column": m.previous('name')}
+                  );
+
+                if (updatedCols.length > 0) {
+                  /*
+                   * Table column name has changed so update
+                   * column name in foreign key as well.
+                   */
+                  updatedCols[0].set(
+                  {"column": m.get('name')});
+                }
+
+                setTimeout(function () {
+                  self.render();
+                }, 10);
+            },
+            formatter: {
+              fromRaw: function (rawValue, model) {
+                return rawValue.pluck("column").toString();
+              },
+              toRaw: function (val, model) {
+                return val;
+              }
+            },
+            render: function() {
+              return Backgrid.StringCell.prototype.render.apply(this, arguments);
+            },
+            remove: function() {
+              var tableCols = this.model.top.get('columns'),
+                  cols = this.model.get('columns');
+              if (cols) {
+                cols.off('pgadmin:columns:updated');
+              }
+
+              this.stopListening(tableCols, 'remove' , self.removeColumn);
+              this.stopListening(tableCols, 'change:name' , self.resetColOptions);
+              this.stopListening(tableCols, 'change:cltype' , self.removeColumnWithType);
+
+              Backgrid.StringCell.prototype.remove.apply(this, arguments);
+            }
+          }),
+        }],
+        validate: function() {
+          this.errorModel.clear();
+          var columns = this.get('columns'),
+              name = this.get('name');
+
+          if ((_.isUndefined(name) || _.isNull(name) || name.length < 1)) {
+            var msg = gettext('Please specify name for exclusion constraint.');
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+          else  if ((_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+            var msg = gettext('Please specify columns for exclusion constraint.');
+            this.errorModel.set('columns', msg);
+            return msg;
+          }
+
+          return null;
+        }
+      }),
+
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [],
+            immediate_parent_table_found = false,
+            is_immediate_parent_table_partitioned = false;
+          // To iterate over tree to check parent node
+          while (i) {
+            // If table is partitioned table then return false
+            if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
+              immediate_parent_table_found = true;
+              if ('is_partitioned' in d && d.is_partitioned) {
+                is_immediate_parent_table_partitioned = true;
+              }
+            }
+
+            // If it is schema then allow user to create table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return !is_immediate_parent_table_partitioned;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return !is_immediate_parent_table_partitioned;
+          }
+      }
+    });
+  }
+
+  return pgBrowser.Nodes['exclusion_constraint'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py
index be306109..ef466774 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/__init__.py
@@ -94,6 +94,14 @@ class ForeignKeyConstraintModule(ConstraintTypeModule):
         return database.DatabaseModule.NODE_TYPE
 
     @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
+    @property
     def csssnippets(self):
         """
         Returns a snippet of css to include in the page
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
new file mode 100644
index 00000000..7d742632
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
@@ -0,0 +1,1102 @@
+define('pgadmin.node.foreign_key', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'sources/alerts/alertify_wrapper',
+
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, AlertifyWrapper) {
+
+  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 class="wcTabIcon ' + optimage + '"/>' + opt.text + '</span>'
+          );
+        }
+      },
+      headerSelectControlTemplate = _.template([
+        '<div class="<%=Backform.controlsClassName%> <%=extraClasses.join(\' \')%>">',
+        '  <select class="pgadmin-node-select form-control" name="<%=name%>" style="width:100%;" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+        '    <%=select2.first_empty ? " <option></option>" : ""%>',
+        '    <% for (var i=0; i < options.length; i++) { %>',
+        '    <% var option = options[i]; %>',
+        '    <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> value=<%= formatter.fromRaw(option.value) %> <%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>',
+        '    <% } %>',
+        '  </select>',
+        '</div>'].join("\n")
+      );
+
+  var ForeignKeyColumnModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      local_column: undefined,
+      references: undefined,
+      referenced: undefined
+    },
+    schema: [{
+        id: 'local_column', label:'Local', type:'text', editable: false,
+        cellHeaderClasses: 'width_percent_50', cell:'string',
+        headerCell: Backgrid.Extension.CustomHeaderCell
+      },{
+        id: 'referenced', label:'Referenced', type: 'text', editable: false,
+        cell:'string', cellHeaderClasses: 'width_percent_50',
+        headerCell: Backgrid.Extension.CustomHeaderCell
+      }]
+  });
+
+  var ForeignKeyColumnControl =  Backform.ForeignKeyColumnControl =
+    Backform.UniqueColCollectionControl.extend({
+
+    initialize: function(opts) {
+      Backform.UniqueColCollectionControl.prototype.initialize.apply(
+        this, arguments
+          );
+
+      var self = this,
+        node = 'foreign_key',
+        headerSchema = [{
+          id: 'local_column', label:'', type:'text',
+          node: 'column', control: Backform.NodeListByNameControl.extend({
+            initialize: function() {
+              // Here we will decide if we need to call URL
+              // Or fetch the data from parent columns collection
+                if(self.model.handler) {
+                  Backform.Select2Control.prototype.initialize.apply(this, arguments);
+                  // Do not listen for any event(s) for existing constraint.
+                  if (_.isUndefined(self.model.get('oid'))) {
+                    var tableCols = self.model.top.get('columns');
+                    this.listenTo(tableCols, 'remove' , this.removeColumn);
+                    this.listenTo(tableCols, 'change:name', this.resetColOptions);
+                  }
+
+                  this.custom_options();
+                } else {
+                  Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+                }
+            },
+            removeColumn: function (m) {
+              var that = this;
+              setTimeout(function   () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            resetColOptions: function(m) {
+              var that = this;
+
+              if (m.previous('name') ==  self.headerData.get('local_column')) {
+                /*
+                 * Table column name has changed so update
+                 * column name in foreign key as well.
+                 */
+                self.headerData.set(
+                  {"local_column": m.get('name')});
+                  self.headerDataChanged();
+              }
+
+              setTimeout(function () {
+                that.custom_options();
+                that.render.apply(that);
+              }, 50);
+            },
+            custom_options: function() {
+              // We will add all the columns entered by user in table model
+              var columns = self.model.top.get('columns'),
+                  added_columns_from_tables = [];
+
+              if (columns.length > 0) {
+                _.each(columns.models, function(m) {
+                    var col = m.get('name');
+                    if(!_.isUndefined(col) && !_.isNull(col)) {
+                      added_columns_from_tables.push(
+                        {label: col, value: col, image:'icon-column'}
+                      );
+                    }
+                });
+              }
+              // Set the values in to options so that user can select
+              this.field.set('options', added_columns_from_tables);
+            },
+            template: headerSelectControlTemplate,
+            remove: function () {
+              if(self.model.handler) {
+                var tableCols = self.model.top.get('columns');
+                this.stopListening(tableCols, 'remove' , this.removeColumn);
+                this.stopListening(tableCols, 'change:name' , this.resetColOptions);
+
+                Backform.Select2Control.prototype.remove.apply(this, arguments);
+
+              } else {
+                Backform.NodeListByNameControl.prototype.remove.apply(this, arguments);
+              }
+            }
+          }),
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: gettext('Select column'),
+            first_empty: !_.isUndefined(self.model.get('oid'))
+          },
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        },{
+          id: 'references', label:'', type: 'text', cache_level: 'server',
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select foreign table',
+          }, first_empty: true,
+          control: Backform.NodeListByNameControl.extend({
+            formatter: Backform.ControlFormatter,
+            template: headerSelectControlTemplate
+            }),
+          url: 'all_tables', node: 'table',
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          },
+          transform: function(rows) {
+            var res = [];
+            _.each(rows, function(r) {
+              res.push({
+                'value': r.value,
+                'image': "icon-table",
+                'label': r.label
+              });
+            });
+            return res;
+          }
+        },{
+          id: 'referenced', label:'', type: 'text', cache_level: 'server',
+          transform: function(rows) {
+            var res = [];
+            _.each(rows, function(r) {
+              res.push({
+                'value': r.name,
+                'image': 'icon-column',
+                'label': r.name
+              });
+            });
+            return res;
+          },
+          control: Backform.Select2Control.extend({
+            formatter: Backform.ControlFormatter,
+            template: headerSelectControlTemplate,
+            render: function() {
+              var self = this,
+              url = self.field.get('url') || self.defaults.url,
+              m = self.model,
+              tid = m.get('references');
+
+              // Clear any existing value before setting new options.
+              m.set(self.field.get('name'), null, {silent: true});
+
+              if (url && !_.isUndefined(tid) && !_.isNull(tid) && tid != '') {
+                var node = this.field.get('schema_node'),
+                    node_info = this.field.get('node_info'),
+                    full_url = node.generate_url.apply(
+                      node, [
+                        null, url, this.field.get('node_data'),
+                        this.field.get('url_with_id') || false, node_info
+                      ]),
+                    data = [];
+
+                if (this.field.get('version_compatible')) {
+                  m.trigger('pgadmin:view:fetching', m, self.field);
+                  $.ajax({
+                    async: false,
+                    data : {tid:tid},
+                    url: full_url,
+                    success: function(res) {
+                      data = res.data;
+                    },
+                    error: function() {
+                      m.trigger('pgadmin:view:fetch:error', m, self.field);
+                    }
+                  });
+                  m.trigger('pgadmin:view:fetched', m, self.field);
+                }
+                /*
+                 * 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);
+                }
+              } else {
+                self.field.set('options', []);
+              }
+              Backform.Select2Control.prototype.render.apply(this, arguments);
+              return this;
+            }
+          }), url: 'get_columns',
+          select2: {
+            allowClear: false,
+            width: "style",
+            placeholder: gettext('Select column'),
+            templateResult: formatNode,
+            templateSelection: formatNode
+          },
+          deps:['references'],  node: 'table',
+          version_compatible: self.field.get('version_compatible'),
+          disabled: function(m) {
+            return !_.isUndefined(self.model.get('oid'));
+          }
+        }],
+        headerDefaults = {local_column: null,
+                          references: null,
+                          referenced:null},
+        gridCols = ['local_column', 'references', 'referenced'];
+
+      if ((!self.model.isNew() && _.isUndefined(self.model.handler)) ||
+          (_.has(self.model, 'handler') &&
+          !_.isUndefined(self.model.handler) &&
+          !_.isUndefined(self.model.get('oid')))) {
+        var column = self.collection.first();
+        if (column) {
+          headerDefaults["references"] = column.get("references");
+        }
+      }
+
+      self.headerData = new (Backbone.Model.extend({
+        defaults: headerDefaults,
+        schema: headerSchema
+      }))({});
+
+      var headerGroups = Backform.generateViewSchema(
+          self.field.get('node_info'), self.headerData, 'create',
+          node, self.field.get('node_data')
+          ),
+          fields = [];
+
+      _.each(headerGroups, function(o) {
+        fields = fields.concat(o.fields);
+      });
+
+      self.headerFields = new Backform.Fields(fields);
+      self.gridSchema = Backform.generateGridColumnsFromModel(
+          //null, ForeignKeyColumnModel, 'edit', gridCols
+          self.field.get('node_info'), self.field.get('model'), 'edit',
+          gridCols, self.field.get('schema_node')
+          );
+
+      self.controls = [];
+      self.listenTo(self.headerData, "change", self.headerDataChanged);
+      self.listenTo(self.headerData, "select2", self.headerDataChanged);
+      self.listenTo(self.collection, "add", self.onAddorRemoveColumns);
+      self.listenTo(self.collection, "remove", self.onAddorRemoveColumns);
+    },
+
+    generateHeader: function(data) {
+      var header = [
+        '<div class="subnode-header-form">',
+        ' <div class="container-fluid">',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-column_label%></label>',
+        '   </div>',
+        '   <div class="col-md-6" header="local_column"></div>',
+        '   <div class="col-md-2">',
+        '     <button class="btn-sm btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
+        '   </div>',
+        '  </div>',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-references_label%></label>',
+        '   </div>',
+        '   <div class="col-md-6" header="references"></div>',
+        '  </div>',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-referenced_label%></label>',
+        '   </div>',
+        '   <div class="col-md-6" header="referenced"></div>',
+        '  </div>',
+        ' </div>',
+        '</div>',].join("\n")
+
+      _.extend(data, {
+        column_label: gettext('Local column'),
+        references_label: gettext('References'),
+        referenced_label: gettext('Referencing')
+      });
+
+      var self = this,
+          headerTmpl = _.template(header),
+          $header = $(headerTmpl(data)),
+          controls = this.controls;
+
+      this.headerFields.each(function(field) {
+        var control = new (field.get("control"))({
+          field: field,
+          model: self.headerData
+        });
+
+        $header.find('div[header="' + field.get('name') + '"]').append(
+          control.render().$el
+        );
+
+        controls.push(control);
+      });
+
+      // We should not show add but in properties mode
+      if (data.mode == 'properties') {
+        $header.html('');
+      }
+
+      self.$header = $header;
+
+      return $header;
+    },
+
+    events: _.extend(
+                {}, Backform.UniqueColCollectionControl.prototype.events,
+                {'click button.add': 'addColumns'}
+                ),
+
+    showGridControl: function(data) {
+
+      var self = this,
+          titleTmpl = _.template([
+            "<div class='subnode-header'>",
+            "<label class='control-label'><%-label%></label>",
+            "</div>"].join("\n")),
+          $gridBody =
+            $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
+              // Append titleTmpl only if create/edit mode
+              data.mode !== 'properties' ? titleTmpl({label: data.label}) : ''
+            );
+
+      // Clean up existing grid if any (in case of re-render)
+      if (self.grid) {
+        self.grid.remove();
+      }
+
+      $gridBody.append(self.generateHeader(data));
+
+      var gridSchema = _.clone(this.gridSchema);
+
+      // Insert Delete Cell into Grid
+      if (data.disabled == false && data.canDelete) {
+          gridSchema.columns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: Backgrid.Extension.DeleteCell,
+            editable: false, cell_priority: -1
+          });
+      }
+
+      // Initialize a new Grid instance
+      var grid = self.grid = new Backgrid.Grid({
+        columns: gridSchema.columns,
+        collection: self.collection,
+        className: "backgrid table-bordered"
+      });
+      self.$grid = grid.render().$el;
+
+      $gridBody.append(self.$grid);
+
+      setTimeout(function() {
+        self.headerData.set({
+          'local_column':
+          self.$header.find(
+            'div[header="local_column"] select option:first'
+            ).val(),
+            'referenced':
+          self.$header.find(
+            'div[header="referenced"] select option:first'
+            ).val(),
+            'references':
+          self.$header.find(
+            'div[header="references"] select option:first'
+            ).val()
+            }, {silent:true}
+          );
+      }, 10);
+
+      // Remove unwanted class from grid to display it properly
+      if(data.mode === 'properties')
+        $gridBody.find('.subnode-header-form').removeClass('subnode-header-form');
+
+      // Render node grid
+      return $gridBody;
+    },
+
+    headerDataChanged: function() {
+      var self = this, val,
+          data = this.headerData.toJSON(),
+          inSelected = false,
+          checkVars = ['local_column', 'referenced'];
+
+      if (!self.$header) {
+        return;
+      }
+
+      if (self.control_data.canAdd) {
+        self.collection.each(function(m) {
+          if (!inSelected) {
+            _.each(checkVars, function(v) {
+              if (!inSelected) {
+                val = m.get(v);
+                inSelected = ((
+                  (_.isUndefined(val) || _.isNull(val)) &&
+                  (_.isUndefined(data[v]) || _.isNull(data[v]))
+                  ) ||
+                  (val == data[v]));
+              }
+            });
+          }
+        });
+      }
+      else {
+        inSelected = true;
+      }
+
+      self.$header.find('button.add').prop('disabled', inSelected);
+    },
+
+    addColumns: function(ev) {
+      ev.preventDefault();
+      var self = this,
+          local_column = self.headerData.get('local_column'),
+          referenced = self.headerData.get('referenced');
+
+      if (!local_column || local_column == '' ||
+          !referenced || referenced  =='') {
+        return false;
+      }
+
+      var m = new (self.field.get('model'))(
+                self.headerData.toJSON()),
+          coll = self.model.get(self.field.get('name'));
+
+      coll.add(m);
+
+      var idx = coll.indexOf(m);
+
+      // idx may not be always > -1 because our UniqueColCollection may
+      // remove 'm' if duplicate value found.
+      if (idx > -1) {
+        self.$grid.find('.new').removeClass('new');
+
+        var newRow = self.grid.body.rows[idx].$el;
+
+        newRow.addClass("new");
+        $(newRow).pgMakeVisible('backform-tab');
+      } else {
+        //delete m;
+      }
+
+      return false;
+    },
+
+    onAddorRemoveColumns: function() {
+      var self = this;
+
+      // Wait for collection to be updated before checking for the button to be
+      // enabled, or not.
+      setTimeout(function() {
+        if (self.collection.length > 0) {
+          self.$header.find(
+            'div[header="references"] select'
+            ).prop('disabled', true);
+        } else {
+          self.$header.find(
+            'div[header="references"] select'
+            ).prop('disabled', false);
+        }
+
+        self.collection.trigger('pgadmin:columns:updated', self.collection);
+
+        self.headerDataChanged();
+
+        if ((!_.has(self.model, 'handler') || (_.has(self.model, 'handler') &&
+              _.isUndefined(self.model.handler))) ||
+            (_.has(self.model, 'handler') && !_.isUndefined(self.model.handler) &&
+            !_.isUndefined(self.model.handler.get('oid')))) {
+          self.getCoveringIndex();
+        }
+
+      }, 10);
+    },
+
+    getCoveringIndex: function() {
+
+      var self = this,
+          url = 'get_coveringindex',
+          m = self.model,
+          cols = [],
+          coveringindex = null;
+
+          self.collection.each(function(m){
+            cols.push(m.get('local_column'));
+          })
+
+          if (cols.length > 0) {
+            var node = this.field.get('schema_node'),
+              node_info = this.field.get('node_info'),
+              full_url = node.generate_url.apply(
+                node, [
+                  null, url, this.field.get('node_data'),
+                  this.field.get('url_with_id') || false, node_info
+                ]);
+
+            if (this.field.get('version_compatible')) {
+              m.trigger('pgadmin:view:fetching', m, self.field);
+              $.ajax({
+                async: false,
+                data : {cols:JSON.stringify(cols)},
+                url: full_url,
+                success: function(res) {
+                  coveringindex = res.data;
+                },
+                error: function() {
+                  m.trigger('pgadmin:view:fetch:error', m, self.field);
+                }
+              });
+              m.trigger('pgadmin:view:fetched', m, self.field);
+            }
+          }
+
+          if (coveringindex) {
+            m.set('hasindex', true);
+            m.set('autoindex', false);
+            m.set('coveringindex', coveringindex);
+          } else {
+            m.set('coveringindex', null);
+            m.set('autoindex', true);
+            m.set('hasindex', false);
+          }
+    },
+
+    remove: function() {
+      /*
+       * Stop listening the events registered by this control.
+       */
+      this.stopListening(this.headerData, "change", this.headerDataChanged);
+      this.listenTo(this.headerData, "select2", this.headerDataChanged);
+      this.listenTo(this.collection, "remove", this.onRemoveVariable);
+      // Remove header controls.
+      _.each(this.controls, function(controls) {
+        controls.remove();
+      });
+
+      ForeignKeyColumnControl.__super__.remove.apply(this, arguments);
+
+      // Remove the header model
+      delete (this.headerData);
+
+    }
+  });
+
+  // Extend the browser's node class for foreign key node
+  if (!pgBrowser.Nodes['foreign_key']) {
+    pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      type: 'foreign_key',
+      label: gettext('Foreign key'),
+      collection_type: 'coll-constraints',
+      sqlAlterHelp: 'ddl-alter.html',
+      sqlCreateHelp: 'ddl-constraints.html',
+      dialogHelp: url_for('help.static', {'filename': 'foreign_key_dialog.html'}),
+      hasSQL: true,
+      parent_type: ['table','partition'],
+      canDrop: true,
+      canDropCascade: true,
+      hasDepends: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_foreign_key_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Foreign key...'),
+          icon: 'wcTabIcon icon-foreign_key', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'validate_foreign_key', node: 'foreign_key', module: this,
+          applies: ['object', 'context'], callback: 'validate_foreign_key',
+          category: 'validate', priority: 4, label: gettext('Validate foreign key'),
+          icon: 'fa fa-link', enable : 'is_not_valid'
+        }
+        ]);
+      },
+      is_not_valid: function(node) {
+        return (node && !node.valid);
+      },
+      callbacks: {
+        validate_foreign_key: function(args) {
+          var input = args || {},
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d) {
+            return false;
+          }
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'validate', d, true),
+            type:'GET',
+            success: function(res) {
+              if (res.success == 1) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(res.info);
+                t.removeIcon(i);
+                data.valid = true;
+                data.icon = 'icon-foreign_key';
+                t.addIcon(i, {icon: data.icon});
+                setTimeout(function() {t.deselect(i);}, 10);
+                setTimeout(function() {t.select(i);}, 100);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(err.errormsg);
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+            });
+
+          return false;
+        }
+      },
+      // Define the model for foreign key node
+      model: pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'oid',
+
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          confmatchtype: undefined,
+          convalidated: undefined,
+          columns: undefined,
+          confupdtype: "a",
+          confdeltype: "a",
+          autoindex: true,
+          coveringindex: undefined,
+          hasindex:undefined
+        },
+        toJSON: function () {
+          var d = pgAdmin.Browser.Node.Model.prototype.toJSON.apply(this, arguments);
+          delete d.hasindex;
+          return d;
+        },
+        // Define the schema for the foreign key node
+        schema: [{
+          id: 'name', label: gettext('Name'), type: 'text',
+          mode: ['properties', 'create', 'edit'], editable:true,
+          headerCell: Backgrid.Extension.CustomHeaderCell, cellHeaderClasses: 'width_percent_50'
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+              if(m.get('comment') && m.get('comment') !== '')
+                 m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'condeferrable', label: gettext('Deferrable?'),
+          type: 'switch', group: gettext('Definition'),
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update condeferrable of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'condeferred', label: gettext('Deferred?'),
+          type: 'switch', group: gettext('Definition'),
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            } else if(!m.isNew()) {
+              return true;
+            }
+            // Disable if condeferred is false or unselected.
+            if(m.get('condeferrable') == true) {
+              return false;
+            } else {
+              setTimeout(function(){
+                if(m.get('condeferred'))
+                  m.set('condeferred', false);
+              },10);
+              return true;
+            }
+          }
+        },{
+          id: 'confmatchtype', label: gettext('Match type'),
+          type: 'switch', group: gettext('Definition'),
+          options: {
+            onText: 'FULL',
+            offText: 'SIMPLE',
+          },disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update condeferred of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'convalidated', label: gettext("Validated?"),
+          type: 'switch', group: gettext('Definition'),
+          options: {
+            onText: gettext('Yes'),
+            offText: gettext('No')
+          },disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed
+                return !(_.isUndefined(m.get('oid')) || m.get("convalidated"));
+            }
+            // We can't update condeferred of existing foreign key.
+            return !(m.isNew() || m.get("convalidated"));
+          }
+        },{
+          id: 'autoindex', label: gettext('Auto FK index?'),
+          type: 'switch', group: gettext('Definition'),
+          deps: ['name', 'hasindex'],
+          options: {
+            onText: gettext('Yes'),
+            offText: gettext('No')
+          },disabled: function(m) {
+            var index = m.get('coveringindex'),
+                autoindex = m.get('autoindex'),
+                setIndexName = function() {
+                  var name = m.get('name'),
+                  oldindex = 'fki_'+m.previous ('name');
+
+                  if (m.get('hasindex')) {
+                    return true;
+                  } else if (m.get('autoindex') && !_.isUndefined(name) && !_.isNull(name) &&
+                              name != '' && (_.isUndefined(index) || _.isNull(index) ||
+                              index == '' || index == oldindex)) {
+                    var newIndex = 'fki_' + name;
+                    m.set('coveringindex', newIndex);
+                    return false;
+                  } else {
+                    return false;
+                  }
+                };
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                if(_.isUndefined(m.get('oid')) && _.isUndefined(m.handler.get('oid'))) {
+                  setTimeout(function () {
+                    if(m.get('autoindex'))
+                      m.set('autoindex', false);
+                  }, 10);
+                  return true;
+                } else {
+                  return setIndexName();
+                }
+            } else {
+              if(!m.isNew() && autoindex && !_.isUndefined(index) &&
+                    !_.isNull(index) && index != '' && m.get('hasindex')) {
+                return true;
+              } else {
+                return setIndexName();
+              }
+            }
+          }
+        },{
+          id: 'coveringindex', label: gettext('Covering index'), type: 'text',
+          mode: ['properties', 'create', 'edit'], group: gettext('Definition'),
+          deps:['autoindex', 'hasindex'],
+          disabled: function(m) {
+            var index = m.get('coveringindex'),
+              setIndexName = function() {
+                if (m.get('hasindex')) {
+                  return true;
+                } else if (!m.get('autoindex')) {
+                  setTimeout(function () {
+                    m.set('coveringindex', null);
+                  });
+                  return true;
+                } else {
+                  setTimeout(function () {
+                    var name = m.get('name'),
+                      newIndex = 'fki_' + name;
+
+                      if (m.get('autoindex') && !_.isUndefined(name) && !_.isNull(name) &&
+                            name != '') {
+                        m.set('coveringindex', newIndex);
+                      }
+                  });
+
+                  return false;
+                }
+              };
+
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                if (_.isUndefined(m.get('oid')) && _.isUndefined(m.handler.get('oid'))) {
+                  return true;
+                } else {
+                  return setIndexName();
+                }
+
+            } else if (!m.isNew() && m.get('autoindex') && !_.isUndefined(index)
+                        && _.isNull(index) && index == '') {
+              return true;
+            }
+
+            return setIndexName();
+          }
+        },{
+          id: 'columns', label: gettext('Columns'),
+          type: 'collection', group: gettext('Columns'), disabled: false,
+          node: 'foreign_key', editable: false, headerCell: Backgrid.Extension.CustomHeaderCell,
+          cellHeaderClasses: 'width_percent_50',
+          cell: Backgrid.StringCell.extend({
+            initialize: function() {
+              Backgrid.StringCell.prototype.initialize.apply(this, arguments);
+              var self = this,
+                collection = this.model.get('columns');
+              // Do not listen for any event(s) for existing constraint.
+              if (_.isUndefined(self.model.get('oid'))) {
+                var tableCols = self.model.top.get('columns');
+                self.listenTo(tableCols, 'remove' , self.removeColumn);
+                self.listenTo(tableCols, 'change:name', self.resetColOptions);
+              }
+
+              self.model.get('columns').on('pgadmin:columns:updated', function() {
+                self.render.apply(self);
+              });
+              self.listenTo(collection, "add", self.render);
+              self.listenTo(collection, "remove", self.render);
+            },
+            removeColumn: function(m){
+              var self = this,
+                  removedCols = self.model.get('columns').where(
+                    {local_column: m.get('name')}
+                    );
+
+              self.model.get('columns').remove(removedCols);
+              setTimeout(function () {
+                self.render();
+              }, 10);
+
+              setTimeout(function () {
+                var constraints = self.model.top.get("foreign_key");
+                var removed = [];
+                constraints.each(function(constraint) {
+                  if (constraint.get("columns").length == 0) {
+                     removed.push(constraint);
+                  }
+                });
+                constraints.remove(removed);
+              },100);
+            },
+            resetColOptions : function(m) {
+                var self = this,
+                  updatedCols = self.model.get('columns').where(
+                  {"local_column": m.previous('name')}
+                  );
+                if (updatedCols.length > 0) {
+                  /*
+                   * Table column name has changed so update
+                   * column name in foreign key as well.
+                   */
+                  updatedCols[0].set(
+                  {"local_column": m.get('name')});
+                }
+
+                setTimeout(function () {
+                  self.render();
+                }, 10);
+            },
+            formatter: {
+              fromRaw: function (rawValue, model) {
+                var cols = [],
+                    remote_cols = [];
+                if (rawValue.length > 0) {
+                  rawValue.each(function(col){
+                    cols.push(col.get('local_column'));
+                    remote_cols.push(col.get('referenced'))
+                  });
+                  return '('+cols.join(', ')+') -> ('+ remote_cols.join(', ')+')';
+                }
+                return "";
+              },
+              toRaw: function (val, model) {
+                return val;
+              }
+            },
+            render: function() {
+              return Backgrid.StringCell.prototype.render.apply(this, arguments);
+            },
+            remove: function() {
+              var tableCols = this.model.top.get('columns');
+
+              this.stopListening(tableCols, 'remove' , self.removeColumn);
+              this.stopListening(tableCols, 'change:name' , self.resetColOptions);
+
+              Backgrid.StringCell.prototype.remove.apply(this, arguments);
+            }
+          }),
+          canAdd: function(m) {
+            // We can't update columns of existing foreign key.
+            return m.isNew();
+          }, canDelete: true,
+          control: ForeignKeyColumnControl,
+          model: ForeignKeyColumnModel,
+          disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update columns of existing foreign key.
+            return !m.isNew();
+            }
+        },{
+          id: 'confupdtype', label: gettext('On update'),
+          type:"select2", group: gettext('Action'), mode: ['edit','create'],
+          select2:{width:"50%", allowClear: false},
+          options: [
+            {label: "NO ACTION", value: "a"},
+            {label: "RESTRICT", value: "r"},
+            {label: "CASCADE", value: "c"},
+            {label: "SET NULL", value: "n"},
+            {label: "SET DEFAULT", value: "d"}
+          ],disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update confupdtype of existing foreign key.
+            return !m.isNew();
+          }
+        },{
+          id: 'confdeltype', label: gettext('On delete'),
+          type:"select2", group: gettext('Action'), mode: ['edit','create'],
+          select2:{width:"50%", allowClear: false},
+          options: [
+            {label: "NO ACTION", value: "a"},
+            {label: "RESTRICT", value: "r"},
+            {label: "CASCADE", value: "c"},
+            {label: "SET NULL", value: "n"},
+            {label: "SET DEFAULT", value: "d"}
+          ],disabled: function(m) {
+            // If we are in table edit mode then
+            if (_.has(m, 'handler') && !_.isUndefined(m.handler)) {
+                // If OID is undefined then user is trying to add
+                // new constraint which should allowed for Unique
+                return !_.isUndefined(m.get('oid'));
+            }
+            // We can't update confdeltype of existing foreign key.
+            return !m.isNew();
+          }
+        }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+
+          var columns = this.get('columns');
+          if ((_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+            var msg = gettext('Please specify columns for Foreign key.');
+            this.errorModel.set('columns', msg);
+            return msg;
+          }
+
+          var coveringindex = this.get('coveringindex'),
+              autoindex = this.get('autoindex');
+          if (autoindex && (_.isUndefined(coveringindex) || _.isNull(coveringindex) ||
+            String(coveringindex).replace(/^\s+|\s+$/g, '') == '')) {
+            var msg = gettext('Please specify covering index name.');
+            this.errorModel.set('coveringindex', msg);
+            return msg;
+          }
+
+          return null;
+        }
+      }),
+
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [],
+            immediate_parent_table_found = false,
+            is_immediate_parent_table_partitioned = false;
+          // To iterate over tree to check parent node
+          while (i) {
+            // If table is partitioned table then return false
+            if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
+              immediate_parent_table_found = true;
+              if ('is_partitioned' in d && d.is_partitioned) {
+                is_immediate_parent_table_partitioned = true;
+              }
+            }
+
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return !is_immediate_parent_table_partitioned;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return !is_immediate_parent_table_partitioned;
+          }
+      }
+    });
+  }
+
+  return pgBrowser.Nodes['foreign_key'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py
index e7a12cce..f8894cd6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py
@@ -93,6 +93,14 @@ class IndexConstraintModule(ConstraintTypeModule):
         """
         return database.DatabaseModule.NODE_TYPE
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 class PrimaryKeyConstraintModule(IndexConstraintModule):
     """
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
new file mode 100644
index 00000000..dc38bbae
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
@@ -0,0 +1,56 @@
+define('pgadmin.node.constraints', [
+  'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+  'pgadmin.browser', 'pgadmin.browser.collection', 'pgadmin.node.unique_constraint',
+  'pgadmin.node.check_constraint', 'pgadmin.node.foreign_key',
+  'pgadmin.node.exclusion_constraint', 'pgadmin.node.primary_key'
+], function(gettext, $, _, S, pgAdmin, pgBrowser) {
+
+  if (!pgBrowser.Nodes['coll-constraints']) {
+    var databases = pgAdmin.Browser.Nodes['coll-constraints'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'constraints',
+        label: gettext('Constraints'),
+        type: 'coll-constraints',
+        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+        columns: ['name', 'comment']
+      });
+  };
+
+  if (!pgBrowser.Nodes['constraints']) {
+    pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      type: 'constraints',
+      label: gettext('Constraints'),
+      collection_type: 'coll-constraints',
+      parent_type: ['table','partition'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([]);
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined
+       },
+        schema: [{
+          id: 'name', label: gettext('Name'), type: 'text',
+          mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label: gettext('Oid'), cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit']
+        }]
+      })
+  });
+  }
+
+  return pgBrowser.Nodes['constraints'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
index 13f9d864..670dccdb 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
@@ -127,6 +127,14 @@ class IndexesModule(CollectionNodeModule):
         """
         return False
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = IndexesModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
new file mode 100644
index 00000000..0bb2d40f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
@@ -0,0 +1,515 @@
+define('pgadmin.node.index', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'backform', 'alertify',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+
+  if (!pgBrowser.Nodes['coll-index']) {
+    var databases = pgAdmin.Browser.Nodes['coll-index'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'index',
+        label: gettext('Indexes'),
+        type: 'coll-index',
+        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+        sqlAlterHelp: 'sql-alterindex.html',
+        sqlCreateHelp: 'sql-createindex.html',
+        dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}),
+        columns: ['name', 'description'],
+        hasStatistics: true,
+        statsPrettifyFields: ['Size', 'Index size']
+      });
+  };
+
+  // Node-Ajax-Cell with Deps
+  var NodeAjaxOptionsDepsCell = Backgrid.Extension.NodeAjaxOptionsCell.extend({
+      initialize: function() {
+        Backgrid.Extension.NodeAjaxOptionsCell.prototype.initialize.apply(this, arguments);
+        Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
+      },
+      dependentChanged: function () {
+        var model = this.model,
+          column = this.column,
+          editable = this.column.get("editable"),
+          input = this.$el.find('select').first();
+
+        var is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
+        if (is_editable) {
+           this.$el.addClass("editable");
+           input.prop('disabled', false);
+         } else {
+           this.$el.removeClass("editable");
+           input.prop('disabled', true);
+         }
+
+        this.delegateEvents();
+        return this;
+      },
+      remove: Backgrid.Extension.DependentCell.prototype.remove
+    });
+
+
+    // Model to create column collection control
+    var ColumnModel = pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          colname: undefined,
+          collspcname: undefined,
+          op_class: undefined,
+          sort_order: false,
+          nulls: false,
+          is_sort_nulls_applicable: true
+        },
+        schema: [
+          {
+            id: 'colname', label: gettext('Column'), cell: 'node-list-by-name',
+            type: 'text', disabled: 'inSchemaWithModelCheck', editable: function(m) {
+                // Header cell then skip
+                if (m instanceof Backbone.Collection) {
+                    return false;
+                }
+                return !(m.inSchemaWithModelCheck.apply(this, arguments));
+            },
+            control: 'node-list-by-name', node: 'column'
+          },{
+            id: 'collspcname', label: gettext('Collation'),
+            cell: NodeAjaxOptionsDepsCell,
+            type: 'text', disabled: 'inSchemaWithModelCheck', editable: function(m) {
+                // Header cell then skip
+                if (m instanceof Backbone.Collection) {
+                    return false;
+                }
+                return !(m.inSchemaWithModelCheck.apply(this, arguments));
+            },
+            control: 'node-ajax-options', url: 'get_collations', node: 'index'
+          },{
+            id: 'op_class', label: gettext('Operator class'),
+            cell: NodeAjaxOptionsDepsCell, tags: true,
+            type: 'text', disabled: 'checkAccessMethod',
+            editable: function(m) {
+                // Header cell then skip
+                if (m instanceof Backbone.Collection) {
+                  return false;
+                } else if (m.inSchemaWithModelCheck.apply(this, arguments)) {
+                  return false;
+                }
+                return !(m.checkAccessMethod.apply(this, arguments));
+            },
+            control: 'node-ajax-options', url: 'get_op_class', node: 'index',
+            deps: ['amname'], transform: function(data, control) {
+             /* We need to extract data from collection according
+              * to access method selected by user if not selected
+              * send btree related op_class options
+              */
+             var amname = control.model.top.get('amname'),
+                 options = data['btree'];
+
+             if(_.isUndefined(amname))
+               return options;
+
+             _.each(data, function(v, k) {
+                if(amname === k) {
+                  options = v;
+                }
+              });
+             return options;
+            }
+          },{
+            id: 'sort_order', label: gettext('Sort order'),
+            cell: Backgrid.Extension.TableChildSwitchCell, type: 'switch',
+            editable: function(m) {
+              // Header cell then skip
+              if (m instanceof Backbone.Collection) {
+                  return false;
+              } else if (m.inSchemaWithModelCheck.apply(this, arguments)) {
+                  return false;
+              } else if (m.top.get('amname') === 'btree') {
+                m.set('is_sort_nulls_applicable', true);
+                return true;
+              } else {
+                m.set('is_sort_nulls_applicable', false);
+                return false;
+              }
+            },
+            deps: ['amname'],
+            options: {
+             'onText': 'DESC', 'offText': 'ASC',
+             'onColor': 'success', 'offColor': 'primary',
+             'size': 'small'
+            }
+          },{
+            id: 'nulls', label: gettext('NULLs'),
+            cell: Backgrid.Extension.TableChildSwitchCell, type: 'switch',
+            editable: function(m) {
+              // Header cell then skip
+              if (m instanceof Backbone.Collection) {
+                  return false;
+              } else if (m.inSchemaWithModelCheck.apply(this, arguments)) {
+                  return false;
+              } else if (m.top.get('amname') === 'btree') {
+                m.set('is_sort_nulls_applicable', true);
+                return true;
+              } else {
+                m.set('is_sort_nulls_applicable', false);
+                return false;
+              }
+            },
+            deps: ['amname', 'sort_order'],
+            options: {
+             'onText': 'FIRST', 'offText': 'LAST',
+             'onColor': 'success', 'offColor': 'primary',
+             'size': 'small'
+            }
+          }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+
+          if (_.isUndefined(this.get('colname'))
+              || String(this.get('colname')).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('Column Name cannot be empty.');
+            this.errorModel.set('colname', msg);
+            return msg;
+          }
+        },
+        // We will check if we are under schema node
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(m.top.node_info &&  'schema' in m.top.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.top.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // We will check if we are under schema node and added condition
+        checkAccessMethod: function(m) {
+        //Access method is empty or btree then do not disable field
+          var parent_model = m.top;
+          if(_.isUndefined(parent_model.get('amname')) ||
+               _.isNull(parent_model.get('amname')) ||
+               String(parent_model.get('amname')).replace(/^\s+|\s+$/g, '') == '' ||
+               parent_model.get('amname') === 'btree') {
+            // We need to set nulls to true if sort_order is set to desc
+            // nulls first is default for desc
+            if(m.get('sort_order') == true && m.previous('sort_order') ==  false) {
+               setTimeout(function() { m.set('nulls', true) }, 10);
+            }
+          }
+          else {
+            m.set('is_sort_nulls_applicable', false);
+          }
+          return false;
+        }
+    });
+
+  if (!pgBrowser.Nodes['index']) {
+    pgAdmin.Browser.Nodes['index'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      parent_type: ['table', 'view', 'mview', 'partition'],
+      collection_type: ['coll-table', 'coll-view'],
+      sqlAlterHelp: 'sql-alterindex.html',
+      sqlCreateHelp: 'sql-createindex.html',
+      type: 'index',
+      label: gettext('Index'),
+      hasSQL:  true,
+      hasDepends: true,
+      hasStatistics: true,
+      statsPrettifyFields: ['Size', 'Index size'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_index_on_coll', node: 'coll-index', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Index...'),
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index', node: 'index', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Index...'),
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Index...'),
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index_onPartition', node: 'partition', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Index...'),
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_index_onMatView', node: 'mview', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: gettext('Index...'),
+          icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'oid',
+
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          nspname: undefined,
+          tabname: undefined,
+          spcname: undefined,
+          amname: 'btree'
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', disabled: 'inSchema'
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'int', disabled: true, mode: ['edit', 'properties']
+        },{
+          id: 'spcname', label: gettext('Tablespace'), cell: 'string',
+          control: 'node-list-by-name', node: 'tablespace',
+          select2: {'allowClear': true},
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema', filter: function(d) {
+            // If tablespace name is not "pg_global" then we need to exclude them
+            if(d && d.label.match(/pg_global/))
+            {
+              return false;
+            }
+            return true;
+          }
+        },{
+          id: 'amname', label: gettext('Access Method'), cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchemaWithModelCheck', url: 'get_access_methods',
+          group: gettext('Definition'), select2: {'allowClear': true},
+          control: Backform.NodeAjaxOptionsControl.extend({
+            // When access method changes we need to clear columns collection
+            onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              var self = this,
+              // current access method
+              current_am = self.model.get('amname'),
+              // previous access method
+              previous_am = self.model.previous('amname');
+              if (current_am != previous_am && self.model.get('columns').length !== 0) {
+                var msg = gettext('Changing access method will clear columns collection');
+                alertify.confirm(msg, function (e) {
+                    // User clicks Ok, lets clear collection
+                    var column_collection = self.model.get('columns'),
+                      col_length = column_collection.length;
+                    for (var i=(col_length-1);i>=0;i--) {
+                       column_collection.remove(column_collection.models[i]);
+                    }
+                  }, function() {
+                    // User clicks Cancel set previous value again in combo box
+                    setTimeout(function(){
+                      self.model.set('amname', previous_am);
+                    }, 10);
+                });
+              }
+            }
+          })
+        },{
+          id: 'cols', label: gettext('Columns'), cell: 'string',
+          type: 'text', disabled: 'inSchema', mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'fillfactor', label: gettext('Fill factor'), cell: 'string',
+          type: 'int', disabled: 'inSchema', mode: ['create', 'edit', 'properties'],
+          min: 10, max:100, group: gettext('Definition')
+        },{
+          id: 'indisunique', label: gettext('Unique?'), cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          group: gettext('Definition')
+        },{
+          id: 'indisclustered', label: gettext('Clustered?'), cell: 'string',
+          type: 'switch', disabled: 'inSchema',
+          group: gettext('Definition')
+        },{
+          id: 'indisvalid', label: gettext('Valid?'), cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'indisprimary', label: gettext('Primary?'), cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'is_sys_idx', label: gettext('System index?'), cell: 'string',
+          type: 'switch', disabled: true, mode: ['properties']
+        },{
+          id: 'isconcurrent', label: gettext('Concurrent build?'), cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          mode: ['create', 'edit'], group: gettext('Definition')
+        },{
+          id: 'indconstraint', label: gettext('Constraint'), cell: 'string',
+          type: 'text', disabled: 'inSchemaWithModelCheck', mode: ['create', 'edit'],
+          control: 'sql-field', visible: true, group: gettext('Definition')
+        },{
+          id: 'columns', label: 'Columns', type: 'collection', deps: ['amname'],
+          group: gettext('Definition'), model: ColumnModel, mode: ['edit', 'create'],
+          canAdd: function(m) {
+            // We will disable it if it's in 'edit' mode
+            if (m.isNew()) {
+              return true;
+            } else {
+              return false;
+            }
+          },
+          canEdit: false,
+          canDelete: function(m) {
+            // We will disable it if it's in 'edit' mode
+            if (m.isNew()) {
+              return true;
+            } else {
+              return false;
+            }
+          },
+          control: 'unique-col-collection', uniqueCol : ['colname'],
+          columns: ['colname', 'op_class', 'sort_order', 'nulls', 'collspcname']
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+        }
+        ],
+        validate: function(keys) {
+          var err = {},
+              changedAttrs = this.changed,
+              msg = undefined;
+
+          // Nothing to validate
+          if (keys && keys.length == 0) {
+            this.errorModel.clear();
+            return null;
+          } else {
+            this.errorModel.clear();
+          }
+
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+          if (_.isUndefined(this.get('spcname'))
+              || String(this.get('spcname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Tablespace cannot be empty.');
+            this.errorModel.set('spcname', msg);
+            return msg;
+          }
+          if (_.isUndefined(this.get('amname'))
+              || String(this.get('amname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Access method cannot be empty.');
+            this.errorModel.set('amname', msg);
+            return msg;
+          }
+          // Checks if all columns has names
+          var cols = this.get('columns');
+          if(cols && cols.length > 0) {
+             if(!_.every(cols.pluck('colname'))) {
+               msg = gettext('You must specify column name.');
+               this.errorModel.set('columns', msg);
+               return msg;
+             }
+          } else if(cols){
+               msg = gettext('You must specify at least one column.');
+               this.errorModel.set('columns', msg);
+               return msg;
+          }
+          return null;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's system columns
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            } else {
+              // if we are in edit mode
+              if (!_.isUndefined(m.get('attnum')) && m.get('attnum') >= 1 ) {
+                return false;
+              } else {
+                return true;
+              }
+           }
+          }
+          return true;
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [],
+            immediate_parent_table_found = false,
+            is_immediate_parent_table_partitioned = false;
+          // To iterate over tree to check parent node
+          while (i) {
+            // Do not allow creating index on partitioned tables.
+            if (!immediate_parent_table_found &&
+                _.indexOf(['table', 'partition'], d._type) > -1) {
+              immediate_parent_table_found = true;
+              if ('is_partitioned' in d && d.is_partitioned) {
+                is_immediate_parent_table_partitioned = true;
+              }
+            }
+
+            // If it is schema then allow user to create index
+            if (_.indexOf(['schema'], d._type) > -1)
+              return !is_immediate_parent_table_partitioned;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return !is_immediate_parent_table_partitioned;
+          }
+      }
+  });
+ }
+
+  return pgBrowser.Nodes['index'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py
index c318e524..e4c89a5c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py
@@ -121,6 +121,14 @@ class RuleModule(CollectionNodeModule):
 
         return snippets
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 # Create blueprint of RuleModule.
 blueprint = RuleModule(__name__)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
new file mode 100644
index 00000000..19fcad2e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
@@ -0,0 +1,274 @@
+define('pgadmin.node.rule', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'codemirror'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, CodeMirror) {
+
+  /**
+    Create and add a rule collection into nodes
+    @param {variable} label - Label for Node
+    @param {variable} type - Type of Node
+    @param {variable} columns - List of columns to
+      display under under properties.
+   */
+  if (!pgBrowser.Nodes['coll-rule']) {
+    var rules = pgAdmin.Browser.Nodes['coll-rule'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'rule',
+        label: gettext('Rules'),
+        type: 'coll-rule',
+        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+        columns: ["name", "owner", "comment"]
+      });
+  }
+
+
+  /**
+    Create and Add an Rule Node into nodes
+    @param {variable} parent_type - The list of nodes
+    under which this node to display
+    @param {variable} type - Type of Node
+    @param {variable} hasSQL - To show SQL tab
+    @param {variable} canDrop - Adds drop rule option
+    in the context menu
+    @param {variable} canDropCascade - Adds drop Cascade
+    rule option in the context menu
+   */
+  if (!pgBrowser.Nodes['rule']) {
+    pgAdmin.Browser.Nodes['rule'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      parent_type: ['table','view', 'partition'],
+      type: 'rule',
+      sqlAlterHelp: 'sql-alterrule.html',
+      sqlCreateHelp: 'sql-createrule.html',
+      dialogHelp: url_for('help.static', {'filename': 'rule_dialog.html'}),
+      label: gettext('rule'),
+      collection_type: 'coll-table',
+      hasSQL:  true,
+      hasDepends: true,
+      canDrop: function(itemData, item, data){
+        pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
+        if(_.has(itemData, 'label') && itemData.label === '_RETURN')
+          return false;
+        else {
+          return true;
+        }
+      },
+      canDropCascade: function(itemData, item, data){
+        pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data]);
+        if(_.has(itemData, 'label') && itemData.label === '_RETURN')
+          return false;
+        else {
+          return true;
+        }
+      },
+      Init: function() {
+
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        /**
+          Add "create rule" menu option into context and object menu
+          for the following nodes:
+          coll-rule, rule and view and table.
+          @property {data} - Allow create rule option on schema node or
+          system rules node.
+         */
+        pgBrowser.add_menus([{
+          name: 'create_rule_on_coll', node: 'coll-rule', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: gettext('Rule...'),
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 5, label: gettext('Rule...'),
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule', node: 'rule', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: gettext('Rule...'),
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Rule...'),
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_rule', node: 'partition', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Rule...'),
+          icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+
+      /**
+        Define model for the rule node and specify the node
+        properties of the model in schema.
+       */
+      model: pgAdmin.Browser.Node.Model.extend({
+        schema: [{
+          id: 'name', label: gettext('Name'),
+          type: 'text', disabled: function(m) {
+            // disable name field it it is system rule
+            if (m && m.get('name') == "_RETURN") {
+              return true;
+            }
+            if (m.isNew()) {
+              return false;
+            } else if (m.node_info.server.version >= 90400) {
+              return false;
+            }
+            return true;
+          }
+        },
+        {
+          id: 'oid', label: gettext('OID'),
+          type: 'text', disabled: true, mode: ['properties']
+        },
+        {
+          id: 'schema', label:'',
+          type: 'text', visible: false, disabled: function(m) {
+            // It is used while generating sql
+            m.set('schema', m.node_info.schema.label);
+          }
+        },
+        {
+          id: 'view', label:'',
+          type: 'text', visible: false, disabled: function(m){
+
+            // It is used while generating sql
+            m.set('view', this.node_data.label);
+          }
+        },
+        {
+          id: 'event', label: gettext('Event'), control: 'select2',
+          group: gettext('Definition'), type: 'text',
+          select2: {
+            width: '100%',
+            allowClear: false
+          },
+          options:[
+            {label: 'Select', value: 'Select'},
+            {label: 'Insert', value: 'Insert'},
+            {label: 'Update', value: 'Update'},
+            {label: 'Delete', value: 'Delete'}
+          ]
+        },
+        {
+          id: 'do_instead', label: gettext('Do Instead'), group: gettext('Definition'),
+          type: 'switch'
+        },
+        {
+          id: 'condition', label: gettext('Condition'),
+          type: 'text', group: gettext('Definition'),
+          control: Backform.SqlFieldControl
+        },
+        {
+          id: 'statements', label: gettext('Commands'),
+          type: 'text', group: gettext('Definition'),
+          control: Backform.SqlFieldControl
+        },
+        {
+          id: 'system_rule', label: gettext('System rule?'),
+          type: 'switch', mode: ['properties']
+        },
+        {
+          id: 'enabled', label: gettext('Enabled?'),
+          type: 'switch', mode: ['properties']
+        },
+        {
+          id: 'comment', label: gettext('Comment'), cell: 'string', type: 'multiline'
+        }
+        ],
+        validate: function() {
+
+          // Triggers specific error messages for fields
+          var err = {},
+            errmsg,
+            field_name = this.get('name');
+          if (_.isUndefined(field_name) || _.isNull(field_name) ||
+            String(field_name).replace(/^\s+|\s+$/g, '') === '')
+          {
+            err['name'] = gettext('Please specify name.');
+            errmsg = errmsg || err['name'];
+            this.errorModel.set('name', errmsg);
+            return errmsg;
+          }
+          else
+          {
+            this.errorModel.unset('name');
+          }
+          return null;
+        }
+      }),
+
+      // Show or hide create rule menu option on parent node
+      canCreate: function(itemData, item, data) {
+
+        // If check is false then , we will allow create menu
+        if (data && data.check === false)
+          return true;
+
+        var t = pgBrowser.tree, i = item, d = itemData;
+
+        // To iterate over tree to check parent node
+        while (i) {
+
+          // If it is schema then allow user to create rule
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-rule' == d._type) {
+
+            //Check if we are not child of rule
+            var prev_i = t.hasParent(i) ? t.parent(i) : null,
+              prev_d = prev_i ? t.itemData(prev_i) : null,
+              prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null,
+              prev_e = prev_j ? t.itemData(prev_j) : null,
+              prev_k = t.hasParent(prev_j) ? t.parent(prev_j) : null,
+              prev_f = prev_k ? t.itemData(prev_k) : null;
+            if( prev_f._type == 'catalog') {
+              return false;
+            } else {
+              return true;
+            }
+          }
+
+          /**
+            Check if it is view and its parent node is schema
+            then allow to create Rule
+           */
+          else if('view' == d._type || 'table' == d._type){
+            prev_i = t.hasParent(i) ? t.parent(i) : null;
+            prev_d = prev_i ? t.itemData(prev_i) : null;
+            prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null;
+            prev_e = prev_j ? t.itemData(prev_j) : null;
+            if(prev_e._type == 'schema') {
+              return true;
+            }else{
+              return false;
+            }
+          }
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+
+        // By default we do not want to allow create menu
+        return true;
+
+      }
+
+  });
+  }
+
+  return pgBrowser.Nodes['coll-rule'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
index 5cbcc4db..19dcf8af 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
@@ -115,6 +115,14 @@ class TriggerModule(CollectionNodeModule):
         return False
 
     @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
+    @property
     def csssnippets(self):
         """
         Returns a snippet of css to include in the page
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
new file mode 100644
index 00000000..87f1eb33
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
@@ -0,0 +1,634 @@
+define('pgadmin.node.trigger', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'backform', 'alertify',
+  'sources/alerts/alertify_wrapper',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify, AlertifyWrapper) {
+
+  var CustomSwitchControl = Backform.CustomSwitchControl = Backform.SwitchControl.extend({
+    template: _.template([
+      '<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
+      '<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
+      '  <div class="checkbox">',
+      '    <label>',
+      '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>"',
+      '        name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%>',
+      '        <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+      '    </label>',
+      '  </div>',
+      '</div>',
+      '<% if (helpMessage && helpMessage.length) { %>',
+      '  <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+      '<% } %>'
+    ].join("\n")),
+    className: 'pgadmin-control-group form-group col-xs-6'
+  });
+
+  if (!pgBrowser.Nodes['coll-trigger']) {
+    var triggers = pgAdmin.Browser.Nodes['coll-trigger'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'trigger',
+        label: gettext('Triggers'),
+        type: 'coll-trigger',
+        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+        columns: ['name', 'description']
+      });
+  };
+
+  if (!pgBrowser.Nodes['trigger']) {
+    pgAdmin.Browser.Nodes['trigger'] = pgBrowser.Node.extend({
+      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+      parent_type: ['table', 'view', 'partition'],
+      collection_type: ['coll-table', 'coll-view'],
+      type: 'trigger',
+      label: gettext('Trigger'),
+      hasSQL:  true,
+      hasDepends: true,
+      width: '650px',
+      sqlAlterHelp: 'sql-altertrigger.html',
+      sqlCreateHelp: 'sql-createtrigger.html',
+      dialogHelp: url_for('help.static', {'filename': 'trigger_dialog.html'}),
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_trigger_on_coll', node: 'coll-trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Trigger...'),
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Trigger...'),
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_trigger_onTable', node: 'table', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Trigger...'),
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_trigger_onPartition', node: 'partition', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Trigger...'),
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'enable_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'enable_trigger',
+          category: 'connect', priority: 3, label: gettext('Enable trigger'),
+          icon: 'fa fa-check', enable : 'canCreate_with_trigger_enable'
+        },{
+          name: 'disable_trigger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'disable_trigger',
+          category: 'drop', priority: 3, label: gettext('Disable trigger'),
+          icon: 'fa fa-times', enable : 'canCreate_with_trigger_disable'
+        },{
+          name: 'create_trigger_onView', node: 'view', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Trigger...'),
+          icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }
+        ]);
+      },
+      callbacks: {
+        /* Enable trigger */
+        enable_trigger: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'enable' , d, true),
+            type:'PUT',
+            data: {'enable' : true},
+            dataType: "json",
+            success: function(res) {
+              if (res.success == 1) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(res.info);
+                t.removeIcon(i);
+                data.icon = 'icon-trigger';
+                t.addIcon(i, {icon: data.icon});
+                t.unload(i);
+                t.setInode(i);
+                t.deselect(i);
+                // Fetch updated data from server
+                setTimeout(function() {
+                  t.select(i);
+                }, 10);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(err.errormsg);
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+          })
+        },
+        /* Disable trigger */
+        disable_trigger: function(args) {
+          var input = args || {};
+          obj = this,
+          t = pgBrowser.tree,
+          i = input.item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+          if (!d)
+            return false;
+
+          var data = d;
+          $.ajax({
+            url: obj.generate_url(i, 'enable' , d, true),
+            type:'PUT',
+            data: {'enable' : false},
+            dataType: "json",
+            success: function(res) {
+              if (res.success == 1) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(res.info);
+                t.removeIcon(i);
+                data.icon = 'icon-trigger-bad';
+                t.addIcon(i, {icon: data.icon});
+                t.unload(i);
+                t.setInode(i);
+                t.deselect(i);
+                // Fetch updated data from server
+                setTimeout(function() {
+                  t.select(i);
+                }, 10);
+              }
+            },
+            error: function(xhr, status, error) {
+              try {
+                var err = $.parseJSON(xhr.responseText);
+                if (err.success == 0) {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(err.errormsg);
+                }
+              } catch (e) {}
+              t.unload(i);
+            }
+          })
+        }
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          is_row_trigger: true,
+          fires: 'BEFORE'
+        },
+        schema: [{
+          id: 'name', label: gettext('Name'), cell: 'string',
+          type: 'text', disabled: 'inSchema'
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'int', disabled: true, mode: ['properties']
+        },{
+          id: 'is_enable_trigger', label: gettext('Trigger enabled?'),
+          type: 'switch', disabled: 'inSchema', mode: ['edit', 'properties'],
+          group: gettext('Definition')
+        },{
+          id: 'is_row_trigger', label: gettext('Row trigger?'),
+          type: 'switch', group: gettext('Definition'),
+          mode: ['create','edit', 'properties'],
+          deps: ['is_constraint_trigger'],
+          disabled: function(m) {
+            // Disabled if table is a partitioned table.
+            if (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
+                _.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
+            {
+              setTimeout(function(){
+                  m.set('is_row_trigger', false);
+              },10);
+
+              return true;
+            }
+
+            // If constraint trigger is set to True then row trigger will
+            // automatically set to True and becomes disable
+            var is_constraint_trigger = m.get('is_constraint_trigger');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(is_constraint_trigger) &&
+                is_constraint_trigger === true) {
+                // change it's model value
+                    setTimeout(function() { m.set('is_row_trigger', true) }, 10);
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                // Check if it is row trigger then enabled it.
+                var is_row_trigger = m.get('is_row_trigger');
+                if (!_.isUndefined(is_row_trigger) && m.node_info['server']['server_type'] == 'ppas') {
+                  return false;
+                }
+                // Disable it
+                return true;
+            }
+          }
+        },{
+          id: 'is_constraint_trigger', label: gettext('Constraint trigger?'),
+          type: 'switch', disabled: 'inSchemaWithModelCheck',
+          mode: ['create','edit', 'properties'],
+          group: gettext('Definition'),
+          disabled: function(m) {
+            // Disabled if table is a partitioned table.
+            if (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
+                _.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
+            {
+              setTimeout(function(){
+                  m.set('is_constraint_trigger', false);
+              },10);
+
+              return true;
+            }
+          }
+        },{
+          id: 'tgdeferrable', label: gettext('Deferrable?'),
+          type: 'switch', group: gettext('Definition'),
+          mode: ['create','edit', 'properties'],
+          deps: ['is_constraint_trigger'],
+          disabled: function(m) {
+            // If constraint trigger is set to True then only enable it
+            var is_constraint_trigger = m.get('is_constraint_trigger');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(is_constraint_trigger) &&
+                is_constraint_trigger === true) {
+                    return false;
+                } else {
+                    // If value is already set then reset it to false
+                    if(m.get('tgdeferrable')) {
+                      setTimeout(function() { m.set('tgdeferrable', false) }, 10);
+                    }
+                    return true;
+                }
+            } else {
+                // Disable it
+                return true;
+            }
+          }
+        },{
+          id: 'tginitdeferred', label: gettext('Deferred?'),
+          type: 'switch', group: gettext('Definition'),
+          mode: ['create','edit', 'properties'],
+          deps: ['tgdeferrable', 'is_constraint_trigger'],
+          disabled: function(m) {
+            // If Deferrable is set to True then only enable it
+            var tgdeferrable = m.get('tgdeferrable');
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(!_.isUndefined(tgdeferrable) &&
+                tgdeferrable) {
+                    return false;
+                } else {
+                    // If value is already set then reset it to false
+                    if(m.get('tginitdeferred')) {
+                      setTimeout(function() { m.set('tginitdeferred', false) }, 10);
+                    }
+                    // If constraint trigger is set then do not disable
+                    return m.get('is_constraint_trigger') ? false : true;
+                }
+            } else {
+                // Disable it
+                return true;
+            }
+          }
+        },{
+          id: 'tfunction', label: gettext('Trigger Function'),
+          type: 'text', disabled: 'inSchemaWithModelCheck',
+          mode: ['create','edit', 'properties'], group: gettext('Definition'),
+          control: 'node-ajax-options', url: 'get_triggerfunctions',
+          cache_node: 'trigger_function'
+        },{
+          id: 'tgargs', label: gettext('Arguments'), cell: 'string',
+          group: gettext('Definition'),
+          type: 'text',mode: ['create','edit', 'properties'], deps: ['tfunction'],
+          disabled: function(m) {
+            // We will disable it when EDB PPAS and trigger function is
+            // set to Inline EDB-SPL
+            var tfunction = m.get('tfunction'),
+                server_type = m.node_info['server']['server_type'];
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                if(server_type === 'ppas' &&
+                    !_.isUndefined(tfunction) &&
+                tfunction === 'Inline EDB-SPL') {
+                    // Disable and clear its value
+                    m.set('tgargs', undefined)
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                // Disable it
+                return true;
+            }
+          }
+        },{
+        id: 'fires', label: gettext('Fires'), deps: ['is_constraint_trigger'],
+        mode: ['create','edit', 'properties'], group: gettext('Events'),
+        options: function(control) {
+            var table_options = [
+                {label: "BEFORE", value: "BEFORE"},
+                {label: "AFTER", value: "AFTER"}],
+                view_options = [
+                {label: "BEFORE", value: "BEFORE"},
+                {label: "AFTER", value: "AFTER"},
+                {label: "INSTEAD OF", value: "INSTEAD OF"}];
+            // If we are under table then show table specific options
+            if(_.indexOf(Object.keys(control.model.node_info), 'table') != -1) {
+                return table_options;
+            } else {
+                return view_options;
+            }
+        },
+        // If create mode then by default open composite type
+        control: 'select2', select2: { allowClear: false, width: "100%" },
+        disabled: function(m) {
+        // If contraint trigger is set to True then only enable it
+        var is_constraint_trigger = m.get('is_constraint_trigger');
+        if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+            if(!_.isUndefined(is_constraint_trigger) &&
+            is_constraint_trigger === true) {
+                setTimeout(function() { m.set('fires', 'AFTER') }, 10);
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            // Check if it is row trigger then enabled it.
+            var fires_ = m.get('fires');
+            if (!_.isUndefined(fires_) && m.node_info['server']['server_type'] == 'ppas') {
+              return false;
+            }
+            // Disable it
+            return true;
+        }
+       }
+      },{
+        type: 'nested', control: 'fieldset', mode: ['create','edit', 'properties'],
+        label: gettext('Events'), group: gettext('Events'),
+        schema:[{
+            id: 'evnt_insert', label: gettext('INSERT'),
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: gettext('Events'),
+            control: Backform.CustomSwitchControl,
+            disabled: function(m) {
+                var evn_insert = m.get('evnt_insert');
+                if (!_.isUndefined(evn_insert) && m.node_info['server']['server_type'] == 'ppas')
+                  return false;
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_update', label: gettext('UPDATE'),
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: gettext('Events'),
+            control: Backform.CustomSwitchControl,
+            disabled: function(m) {
+                var evn_update = m.get('evnt_update');
+                if (!_.isUndefined(evn_update) && m.node_info['server']['server_type'] == 'ppas')
+                  return false;
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_delete', label: gettext('DELETE'),
+            type: 'switch', mode: ['create','edit', 'properties'],
+            group: gettext('Events'),
+            control: Backform.CustomSwitchControl,
+            disabled: function(m) {
+                var evn_delete = m.get('evnt_delete');
+                if (!_.isUndefined(evn_delete) && m.node_info['server']['server_type'] == 'ppas')
+                  return false;
+                return m.inSchemaWithModelCheck.apply(this, [m]);
+            }
+        },{
+            id: 'evnt_truncate', label: gettext('TRUNCATE'),
+            type: 'switch', group: gettext('Events'),
+            control: Backform.CustomSwitchControl,
+            disabled: function(m) {
+            var is_constraint_trigger = m.get('is_constraint_trigger'),
+                is_row_trigger = m.get('is_row_trigger'),
+                server_type = m.node_info['server']['server_type'];
+            if(!m.inSchemaWithModelCheck.apply(this, [m])) {
+                // We will enabale truncate only for EDB PPAS
+                // and both triggers row & constarint are set to false
+                if(server_type === 'ppas' &&
+                    !_.isUndefined(is_constraint_trigger) &&
+                    !_.isUndefined(is_row_trigger) &&
+                is_constraint_trigger === false &&
+                    is_row_trigger === false) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                // Disable it
+                return true;
+            }
+        }
+        }]
+        },{
+            id: 'whenclause', label: gettext('When'),
+            type: 'text', disabled: 'inSchemaWithModelCheck',
+            mode: ['create', 'edit', 'properties'],
+            control: 'sql-field', visible: true, group: gettext('Events')
+        },{
+            id: 'columns', label: gettext('Columns'), url: 'nodes',
+            control: 'node-list-by-name', cache_node: 'column', type: 'array',
+            select2: {'multiple': true},
+            deps: ['evnt_update'], node: 'column', group: gettext('Events'),
+            disabled: function(m) {
+                if(this.node_info &&  'catalog' in this.node_info) {
+                    return true;
+                }
+                //Disable in edit mode
+                if (!m.isNew()) {
+                    return true;
+                }
+                // Enable column only if update event is set true
+                var isUpdate = m.get('evnt_update');
+                if(!_.isUndefined(isUpdate) && isUpdate) {
+                    return false;
+                }
+             return true;
+            }
+        },{
+            id: 'prosrc', label: gettext('Code'), group: gettext('Code'),
+            type: 'text', mode: ['create', 'edit'], deps: ['tfunction'],
+            control: 'sql-field', visible: true,
+            disabled: function(m) {
+                // We will enable it only when EDB PPAS and trigger function is
+                // set to Inline EDB-SPL
+                var tfunction = m.get('tfunction'),
+                    server_type = m.node_info['server']['server_type'];
+
+                if(server_type === 'ppas' &&
+                    !_.isUndefined(tfunction) &&
+                    tfunction === 'Inline EDB-SPL')
+                  return false;
+                else
+                  return true;
+            }
+        },{
+          id: 'is_sys_trigger', label: gettext('System trigger?'), cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck', mode: ['properties']
+        },{
+          id: 'is_constarint', label: gettext('Constraint?'), cell: 'string',
+          type: 'switch', disabled: 'inSchemaWithModelCheck', mode: ['properties'],
+          group: gettext('Definition')
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          disabled: 'inSchema'
+    }],
+        validate: function(keys) {
+          var err = {},
+              msg = undefined;
+          this.errorModel.clear();
+
+          // If nothing to validate
+          if (keys && keys.length == 0) {
+            return null;
+          }
+
+          if(_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+            }
+          if(_.isUndefined(this.get('tfunction'))
+              || String(this.get('tfunction')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Trigger function cannot be empty.');
+            this.errorModel.set('tfunction', msg);
+            return msg;
+          }
+
+          if(!this.get('evnt_truncate') && !this.get('evnt_delete') &&
+            !this.get('evnt_update') && !this.get('evnt_insert')) {
+            msg = gettext('Specify at least one event.');
+            this.errorModel.set('evnt_truncate', " ");
+            this.errorModel.set('evnt_delete', " ");
+            this.errorModel.set('evnt_update', " ");
+            this.errorModel.set('evnt_insert', msg);
+            return msg;
+          }
+
+          if(!_.isUndefined(this.get('tfunction')) &&
+            this.get('tfunction') === 'Inline EDB-SPL' &&
+            (_.isUndefined(this.get('prosrc'))
+              || String(this.get('prosrc')).replace(/^\s+|\s+$/g, '') == ''))
+          {
+            msg = gettext('Trigger code cannot be empty.');
+            this.errorModel.set('prosrc', msg);
+            return msg;
+          }
+          return null;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchema: function() {
+          if(this.node_info &&  'catalog' in this.node_info) {
+            return true;
+          }
+          return false;
+        },
+        // We will check if we are under schema node & in 'create' mode
+        inSchemaWithModelCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's in 'edit' mode
+            if (m.isNew()) {
+              return false;
+            } else {
+              return true;
+            }
+          }
+          return true;
+        },
+        // Checks weather to enable/disable control
+        inSchemaWithColumnCheck: function(m) {
+          if(this.node_info &&  'schema' in this.node_info) {
+            // We will disable control if it's system columns
+            // ie: it's position is less then 1
+            if (m.isNew()) {
+              return false;
+            } else {
+              // if we are in edit mode
+              if (!_.isUndefined(m.get('attnum')) && m.get('attnum') >= 1 ) {
+                return false;
+              } else {
+                return true;
+              }
+           }
+          }
+          return true;
+        }
+      }),
+      // Below function will enable right click menu for creating column
+      canCreate: function(itemData, item, data) {
+          // If check is false then , we will allow create menu
+          if (data && data.check == false)
+            return true;
+
+          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to c reate table
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true;
+            parents.push(d._type);
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // If node is under catalog then do not allow 'create' menu
+          if (_.indexOf(parents, 'catalog') > -1) {
+            return false;
+          } else {
+            return true;
+          }
+      },
+      // Check to whether trigger is disable ?
+      canCreate_with_trigger_enable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.icon === 'icon-trigger-bad') {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      },
+      // Check to whether trigger is enable ?
+      canCreate_with_trigger_disable: function(itemData, item, data) {
+        if(this.canCreate.apply(this, [itemData, item, data])) {
+          // We are here means we can create menu, now let's check condition
+          if(itemData.icon === 'icon-trigger') {
+            return true;
+          } else {
+            return false;
+          }
+        }
+      }
+  });
+ }
+
+  return pgBrowser.Nodes['trigger'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/resource_groups/__init__.py b/web/pgadmin/browser/server_groups/servers/resource_groups/__init__.py
index cf945b03..55eaa531 100644
--- a/web/pgadmin/browser/server_groups/servers/resource_groups/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/resource_groups/__init__.py
@@ -100,6 +100,15 @@ class ResourceGroupModule(CollectionNodeModule):
         return servers.ServerModule.NODE_TYPE
 
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
+
 blueprint = ResourceGroupModule(__name__)
 
 
diff --git a/web/pgadmin/browser/server_groups/servers/resource_groups/static/js/resource_group.js b/web/pgadmin/browser/server_groups/servers/resource_groups/static/js/resource_group.js
new file mode 100644
index 00000000..7d370351
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/resource_groups/static/js/resource_group.js
@@ -0,0 +1,126 @@
+define('pgadmin.node.resource_group', [
+  'sources/gettext', 'sources/url_for', 'underscore', 'pgadmin.browser',
+  'pgadmin.browser.collection'
+], function(gettext, url_for, _, pgBrowser) {
+
+  // Extend the browser's collection class for resource group collection
+  if (!pgBrowser.Nodes['coll-resource_group']) {
+    pgBrowser.Nodes['coll-resource_group'] =
+      pgBrowser.Collection.extend({
+        node: 'resource_group',
+        label: gettext('Resource Groups'),
+        type: 'coll-resource_group',
+        columns: ['name', 'cpu_rate_limit', 'dirty_rate_limit']
+    });
+  }
+
+  // Extend the browser's node class for resource group node
+  if (!pgBrowser.Nodes['resource_group']) {
+    pgBrowser.Nodes['resource_group'] = pgBrowser.Node.extend({
+      parent_type: 'server',
+      type: 'resource_group',
+      dialogHelp: url_for('help.static', {'ilename': 'resource_group_dialog.html'}),
+      label: gettext('Resource Group'),
+      hasSQL:  true,
+      canDrop: true,
+      Init: function() {
+
+        // Avoid multiple registration of menus
+        if (this.initialized) {
+          return;
+        }
+
+        this.initialized = true;
+
+        // Creating menu for the resource group node
+        pgBrowser.add_menus([{
+          name: 'create_resourcegroup_on_server', node: 'server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Resource Group...'),
+          icon: 'wcTabIcon icon-resource_group', data: {action: 'create'},
+          /* Function is used to check the server type and version.
+           * Resource Group only supported in PPAS 9.4 and above.
+           */
+          enable: function(node, item) {
+            var treeData = this.getTreeNodeHierarchy(item),
+                server = treeData['server'];
+            return server.connected && node.server_type === 'ppas' &&
+                   node.version >= 90400;
+          }
+        },{
+          name: 'create_resource_group_on_coll', node: 'coll-resource_group', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Resource Group...'),
+          icon: 'wcTabIcon icon-resource_group', data: {action: 'create'}
+        },{
+          name: 'create_resource_group', node: 'resource_group', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Resource Group...'),
+          icon: 'wcTabIcon icon-resource_group', data: {action: 'create'}
+        }
+        ]);
+      },
+
+      // Defining model for resource group node
+      model: pgBrowser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          cpu_rate_limit: 0.0,
+          dirty_rate_limit: 0.0
+        },
+
+        // 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 (%%)'), 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
+        }],
+
+        /* validate function is used to validate the input given by
+         * the user. In case of error, message will be displayed on
+         * the GUI for the respective control.
+         */
+        validate: function(keys) {
+          var msg, cpu_rate_limit, dirty_rate_limit, name,
+              name = this.get('name');
+          if (_.isUndefined(name) || _.isNull(name) ||
+              String(name).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('name');
+          }
+
+          var cpu_rate_limit = this.get('cpu_rate_limit');
+          if (_.isUndefined(cpu_rate_limit) || _.isNull(cpu_rate_limit) ||
+              String(cpu_rate_limit).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('CPU rate limit cannot be empty.');
+            this.errorModel.set('cpu_rate_limit', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('cpu_rate_limit');
+          }
+
+          var dirty_rate_limit = this.get('dirty_rate_limit');
+          if (_.isUndefined(dirty_rate_limit) || _.isNull(dirty_rate_limit) ||
+            String(dirty_rate_limit).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = gettext('Dirty rate limit cannot be empty.');
+            this.errorModel.set('dirty_rate_limit', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('dirty_rate_limit');
+          }
+          return null;
+        }
+      })
+    });
+  }
+
+  return pgBrowser.Nodes['coll-resource_group'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/roles/__init__.py b/web/pgadmin/browser/server_groups/servers/roles/__init__.py
index da8d06a5..a89229ea 100644
--- a/web/pgadmin/browser/server_groups/servers/roles/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/roles/__init__.py
@@ -74,6 +74,14 @@ class RoleModule(CollectionNodeModule):
 
         return snippets
 
+    @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
 
 blueprint = RoleModule(__name__)
 
diff --git a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js
new file mode 100644
index 00000000..44703f18
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js
@@ -0,0 +1,564 @@
+define('pgadmin.node.role', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'pgadmin.backform', 'select2', 'pgadmin.browser.collection',
+  'pgadmin.browser.node.ui', 'pgadmin.browser.server.variable'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify, Backform) {
+
+  if (!pgBrowser.Nodes['coll-role']) {
+    var role = pgAdmin.Browser.Nodes['coll-role'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'role',
+        type: 'coll-role',
+        columns: [
+          'rolname', 'rolvaliduntil', 'rolconnlimit', 'rolcanlogin',
+          'rolsuper', 'rolcreaterole', 'rolcreatedb', 'rolcatupdate',
+          'rolinherit', 'rolreplication'
+        ]
+      });
+  };
+
+  var SecurityModel = pgAdmin.Browser.SecurityModel =
+        pgAdmin.Browser.SecurityModel || pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      provider: null,
+      label: null
+    },
+    schema: [{
+      id: 'provider', label: gettext('Provider'),
+      type: 'text', disabled: false,
+      cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'label', label: gettext('Security Label'),
+      type: 'text', disabled: false,
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null,
+          data = this.toJSON();
+
+      if (_.isUndefined(data.provider) ||
+        _.isNull(data.provider) ||
+        String(data.provider).replace(/^\s+|\s+$/g, '') == '') {
+        var msg = gettext('Please specify the value for all the security providers.');
+
+        this.errorModel.set('provider', msg);
+        return msg;
+      } else {
+        this.errorModel.unset('provider');
+      }
+
+      if (_.isUndefined(data.label) ||
+        _.isNull(data.label) ||
+        String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+        var msg = gettext('Please specify the value for all the security providers.') ;
+
+        this.errorModel.set('label', msg);
+        return msg;
+      } else {
+        this.errorModel.unset('label');
+      }
+
+      return null;
+    }
+  });
+
+  var switchOptions = {
+    'onText': 'Yes', 'offText': 'No',
+    'size': 'mini'
+  };
+
+  var RoleCustomSwitchControl = Backform.SwitchControl.extend({
+    template: _.template([
+      '<label class="control-label pg-el-sm-9 pg-el-xs-12"><%=label%></label>',
+      '<div class="pgadmin-controls pg-el-sm-3 pg-el-xs-12">',
+      '  <div class="checkbox" style="float:right">',
+      '    <label>',
+      '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+      '    </label>',
+      '  </div>',
+      '  <% if (helpMessage && helpMessage.length) { %>',
+      '    <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+      '  <% } %>',
+      '</div>',
+    ].join("\n"))
+  });
+
+  var RoleMembersControl = Backform.Control.extend({
+    defaults: _.defaults(
+                {extraClasses: ['col-xs-12 col-sm-12 col-md-12']},
+                Backform.NodeListByNameControl.prototype.defaults
+                ),
+    initialize: function() {
+      Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+    },
+    formatter: {
+      fromRaw: function (rawData, model) {
+        var res = _.isObject(rawData) ? rawData : JSON.parse(rawData);
+
+        return _.pluck(res, 'role');
+      },
+      toRaw: function (formattedData, model) {
+        return formattedData;
+      }
+    },
+    template: _.template([
+      '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+      '<div class="<%=Backform.controlsClassName%>">',
+      '  <select multiple="multiple" style="width:100%;" class="pgadmin-controls <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-JSON.stringify(value)%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%>>',
+      '    <% for (var i=0; i < options.length; i++) { %>',
+      '      <% var option = options[i]; %>',
+      '      <option value=<%-option.value%> data-icon=<%-option.image%> <%=value != null && _.indexOf(value, option.value) != -1 ? "selected" : ""%> <%=option.disabled ? "disabled=\'disabled\'" : ""%>><%-option.label%></option>',
+      '    <% } %>',
+      '  </select>',
+      '  <% if (helpMessage && helpMessage.length) { %>',
+      '    <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+      '  <% } %>',
+      '</div>',
+      ].join("\n")),
+    selectionTemplate: _.template([
+      '<span>',
+      '  <span class="wcTabIcon <%= optimage %>"></span>',
+      '  <span><%= opttext %><span>',
+      '  <% if (checkbox) { %>',
+      '  <input type="checkbox" <%=disabled ? "disabled=\'disabled\'" : ""%>/>',
+      '  <% } %>',
+      '</span>'
+    ].join("\n")),
+    events: {"change select": "onChange"},
+    getValueFromDOM: function() {
+      var res = [];
+
+      this.$el.find("select").find(':selected').each(function() {
+        res.push($(this).attr('value'));
+      });
+
+      return res;
+    },
+    render: function() {
+      var field = _.defaults(this.field.toJSON(), this.defaults),
+          attributes = this.model.toJSON(),
+          attrArr = field.name.split('.'),
+          name = attrArr.shift(),
+          path = attrArr.join('.'),
+          rawValue = this.keyPathAccessor(attributes[name], path),
+          data = _.extend(field, {
+            rawValue: rawValue,
+            value: this.formatter.fromRaw(rawValue, this.model),
+            attributes: attributes,
+            formatter: this.formatter
+          }),
+          evalF = function(f, d, m) {
+            return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
+          },
+          evalASFunc = function(f, d, m) {
+             return (_.isFunction(f) ? f.apply(d, [m]) : f);
+          };
+
+      // Evaluate the disabled, visible, and required option
+      _.extend(data, {
+        disabled: evalF(data.disabled, data, this.model),
+        visible:  evalF(data.visible, data, this.model),
+        required: evalF(data.required, data, this.model),
+        helpMessage: evalASFunc(data.helpMessage, data, this.model)
+      });
+      // Evaluation the options
+      if (_.isFunction(data.options)) {
+        try {
+          data.options = data.options.apply(this)
+        } catch(e) {
+          // Do nothing
+          data.options = []
+          this.model.trigger('pgadmin-view:transform:error', m, self.field, e);
+        }
+      }
+
+      // Clean up first
+      this.$el.removeClass(Backform.hiddenClassName);
+
+      if (!data.visible)
+        this.$el.addClass(Backform.hiddenClassName);
+
+      this.$el.html(this.template(data)).addClass(field.name);
+      this.updateInvalid();
+
+      var self = this,
+          collection = this.model.get(this.field.get('name')),
+          formatState = function(opt) {
+            if (!opt.id) {
+              return opt.text;
+            }
+
+            var optimage = $(opt.element).data('icon');
+
+            if(!optimage){
+              return opt.text;
+            } else {
+              var d = _.extend(
+                 {}, data, {
+                   'opttext': opt.text,
+                   'optimage': optimage,
+                   'checkbox': false
+                 });
+              return $(self.selectionTemplate(d));
+            }
+          },
+          formatSelection = function (opt) {
+
+            if (!opt.id) {
+              return opt.text;
+            }
+
+            var optimage = $(opt.element).data('icon'),
+                grantUpdate = function(ev) {
+
+                  _.each(collection.where({role: opt.id}), function(m) {
+                    m.set('admin', $(ev.target).is(":checked"));
+                  });
+
+                  return false;
+                };
+
+            if(!optimage){
+              return opt.text;
+            } else {
+              var d = _.extend(
+                 {}, data, {
+                   'opttext': opt.text,
+                   'optimage': optimage,
+                   'checkbox': true
+                 }),
+                 j = $(self.selectionTemplate(d));
+
+              // Update the checkbox lazy
+              setTimeout(
+                function() {
+                  _.each(collection.where({role: opt.id}), function(m) {
+                    j.find('input').prop('checked', m.get('admin'));
+                  });
+                }, 200);
+
+              (j.find('input')).on('change', grantUpdate);
+
+              return j;
+            }
+          };
+
+      this.$el.find('select').select2({
+        templateResult: formatState,
+        templateSelection: formatSelection,
+        multiple: true,
+        tags: true,
+        allowClear: data.disabled ? false : true,
+        placeholder: data.disabled ? "" : gettext("Select members"),
+        width: 'style'
+      }).on("change", function(e) {
+        $(e.target).find(':selected').each(function() {
+        });
+      });
+
+      return this;
+    },
+    onChange: function(e) {
+      var model = this.model,
+          $el = $(e.target),
+          attrArr = this.field.get("name").split('.'),
+          name = attrArr.shift(),
+          path = attrArr.join('.'),
+          vals = this.getValueFromDOM(),
+          collection = model.get(name),
+          removed = [];
+
+      this.stopListening(this.model, "change:" + name, this.render);
+
+      /*
+       * Iterate through all the values, and find out how many are already
+       * present in the collection.
+       */
+      collection.each(function(m) {
+        var role = m.get('role'),
+            idx = _.indexOf(vals, role);
+
+        if (idx > -1) {
+          vals.splice(idx, 1);
+        } else {
+          removed.push(role);
+        }
+      });
+
+      /*
+       * Adding new values
+       */
+      _.each(vals, function(v) {
+        collection.add({role: v});
+      });
+
+      /*
+       * Removing unwanted!
+       */
+      _.each(removed, function(v) {
+        collection.remove(collection.where({role: v}));
+      });
+
+      this.listenTo(this.model, "change:" + name, this.render);
+    }
+  });
+
+  if (!pgBrowser.Nodes['role']) {
+    var role = pgAdmin.Browser.Nodes['role'] = pgAdmin.Browser.Node.extend({
+      parent_type: 'server',
+      type: 'role',
+      sqlAlterHelp: 'sql-alterrole.html',
+      sqlCreateHelp: 'sql-createrole.html',
+      dialogHelp: url_for('help.static', {'filename': 'role_dialog.html'}),
+      label: gettext('Login/Group Role'),
+      hasSQL: true,
+      canDrop: function(node, item) {
+        var treeData = this.getTreeNodeHierarchy(item),
+            server = treeData['server'];
+        /*
+        To Drop a role:
+          1) If Role we are deleting is superuser then User must be superuser
+          2) And for non-superuser roles User must have Create Role permission
+        */
+
+        // Role you are trying to drop is Superuser ?
+        if(node.is_superuser) {
+            return server.connected && server.user.is_superuser;
+        }
+        // For non super users
+        return server.connected && server.user.can_create_role;
+      },
+      hasDepends: true,
+      node_label: function(r) {
+        return r.label;
+      },
+      node_image: function(r) {
+        if (r == null || r == undefined)
+            return 'icon-role';
+        return (r.can_login ? 'icon-role' : 'icon-group');
+      },
+      title: function(d) {
+        if (!d) {
+          return this.label;
+        }
+        if (d.can_login) {
+          return gettext('Login Role') + ' - ' + d.label;
+        }
+        return gettext('Group Role') + ' - ' + d.label;
+      },
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_role_on_server', node: 'server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Login/Group Role...'),
+          icon: 'wcTabIcon icon-role', data: {action: 'create'},
+          enable: 'can_create_role'
+        },{
+          name: 'create_role_on_roles', node: 'coll-role', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Login/Group Role...'),
+          icon: 'wcTabIcon icon-role', data: {action: 'create'},
+          enable: 'can_create_role'
+        },{
+          name: 'create_role', node: 'role', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Login/Group Role...'),
+          icon: 'wcTabIcon icon-role', data: {action: 'create'},
+          enable: 'can_create_role'
+        }]);
+      },
+      can_create_role: function(node, item) {
+        var treeData = this.getTreeNodeHierarchy(item),
+            server = treeData['server'];
+
+        return server.connected && server.user.can_create_role;
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        idAttribute: 'oid',
+        defaults: {
+          oid: null,
+          rolname: undefined,
+          rolcanlogin: false,
+          rolpassword: null,
+          rolconnlimit: -1,
+          rolsuper: false,
+          rolcreaterole: false,
+          rolcreatedb: false,
+          rolinherit: true,
+          rolcatupdate: false,
+          rolreplication: false,
+          rolmembership: [],
+          rolvaliduntil: null,
+          seclabels: [],
+          variables: []
+        },
+        schema: [{
+          id: 'rolname', label: gettext('Name'), type: 'text',
+          disabled: 'readonly'
+        },{
+          id: 'oid', label: gettext('OID'), cell: 'string', mode: ['properties'],
+          editable: false, type: 'text', visible: true, disabled: true
+        },{
+          id: 'rolpassword', label: gettext('Password'), type: 'password',
+          group: gettext('Definition'), mode: ['edit', 'create'],
+          control: 'input', deps: ['rolcanlogin'], retype: true,
+          cell: 'string', disabled: function(m) {
+            if (!m.isNew()) {
+              var user = this.node_info.server.user;
+
+              return (!(user.is_superuser || user.can_create_role) &&
+                  user.id != m.get('oid'));
+            }
+            return false;
+          }
+        },{
+          id: 'rolvaliduntil', disabled: 'readonly', type: 'text',
+          group: gettext('Definition'), label: gettext('Account expires'),
+          mode: ['properties', 'edit', 'create'], control: 'datetimepicker',
+          deps: ['rolcanlogin'], options: {format: 'YYYY-MM-DD HH:mm:ss Z'}
+        },{
+          id: 'rolconnlimit',  type: 'int', group: gettext('Definition'),
+          label: gettext('Connection limit'), cell: 'number', min : -1,
+          mode: ['properties', 'edit', 'create'], disabled: 'readonly'
+        },{
+          id: 'rolcanlogin', label: gettext('Can login?'), type: 'switch',
+          group: gettext('Privileges'), options: switchOptions,
+          disabled: 'readonly', control: RoleCustomSwitchControl
+        },{
+          id: 'rolsuper', label: gettext('Superuser'), type: 'switch',
+          group: gettext('Privileges'), options: switchOptions,
+          control: RoleCustomSwitchControl.extend({
+            onChange: function() {
+              Backform.SwitchControl.prototype.onChange.apply(this, arguments);
+
+              this.model.set('rolcatupdate', this.model.get('rolsuper'));
+              this.model.set('rolcreaterole', this.model.get('rolsuper'));
+              this.model.set('rolcreatedb', this.model.get('rolsuper'));
+            }
+          }),
+          disabled: 'readonly'
+        },{
+          id: 'rolcreaterole', label: gettext('Create roles?'),
+          group: gettext('Privileges'), type: 'switch',
+          options: switchOptions, disabled: 'readonly',
+          control: RoleCustomSwitchControl
+        },{
+          id: 'description', label: gettext('Comments'), type: 'multiline',
+          group: null, mode: ['properties', 'edit', 'create'],
+          options: switchOptions, disabled: 'readonly'
+        },{
+          id: 'rolcreatedb', label: gettext('Create databases?'),
+          group: gettext('Privileges'), type: 'switch',
+          options: switchOptions, disabled: 'readonly',
+          control: RoleCustomSwitchControl
+        },{
+          id: 'rolcatupdate', label: gettext('Update catalog?'),
+          type: 'switch', max_version: 90400, options: switchOptions,
+          control: RoleCustomSwitchControl,
+          group: gettext('Privileges'), disabled: function(m) {
+            return (m.get('read_only') || (!m.get('rolsuper')));
+          }
+        },{
+          id: 'rolinherit', group: gettext('Privileges'),
+          label: gettext('Inherit rights from the parent roles?'),
+          type: 'switch', options: switchOptions, disabled: 'readonly',
+          control: RoleCustomSwitchControl
+        },{
+          id: 'rolreplication', group: gettext('Privileges'),
+          label: gettext('Can initiate streaming replication and backups?'),
+          type: 'switch', min_version: 90100, options: switchOptions,
+          disabled: 'readonly', control: RoleCustomSwitchControl
+        },{
+          id: 'rolmembership', label: gettext('Roles'),
+          group: gettext('Membership'), type: 'collection',
+          cell: 'string', disabled: 'readonly',
+          mode: ['properties', 'edit', 'create'],
+          control: RoleMembersControl, model: pgBrowser.Node.Model.extend({
+            keys: ['role'],
+            idAttribute: 'role',
+            defaults: {
+              role: undefined,
+              admin: false
+            },
+            validate: function() {
+              return null;
+            }
+          }),
+          filter: function(d) {
+            return this.model.isNew() || (this.model.get('rolname') != d.label);
+          },
+          helpMessage: function(m) {
+            if (m.has('read_only') && m.get('read_only') == false) {
+              return gettext('Select the checkbox for roles to include WITH ADMIN OPTION.');
+            } else {
+              return gettext('Roles shown with a check mark have the WITH ADMIN OPTION set.');
+            }
+          }
+        },{
+          id: 'variables', label: gettext('Parameters'), type: 'collection',
+          group: gettext('Parameters'), hasDatabase: true, url: 'variables',
+          model: pgBrowser.Node.VariableModel.extend({keys:['name', 'database']}),
+          control: 'variable-collection',
+          mode: [ 'edit', 'create'], canAdd: true, canDelete: true,
+          url: "variables", disabled: 'readonly'
+        },{
+          id: 'seclabels', label: gettext('Security Labels'),
+          model: SecurityModel, editable: false, type: 'collection',
+          group: gettext('Security'), mode: ['edit', 'create'],
+          min_version: 90200, disabled: 'readonly', canAdd: true,
+          canEdit: false, canDelete: true, control: 'unique-col-collection'
+        }],
+        readonly: function(m) {
+          if (!m.has('read_only')) {
+            var user = this.node_info.server.user;
+
+            m.set('read_only', !(user.is_superuser || user.can_create_role));
+          }
+
+          return m.get('read_only');
+        },
+        validate: function()
+        {
+          var err = {},
+              errmsg,
+              seclabels = this.get('seclabels');
+
+          if (_.isUndefined(this.get('rolname')) || String(this.get('rolname')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = gettext('Name cannot be empty.');
+            errmsg = errmsg || err['name'];
+          }
+
+          if (seclabels) {
+            var secLabelsErr;
+            for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) {
+              secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]);
+              if (secLabelsErr) {
+                err['seclabels'] = secLabelsErr;
+                errmsg = errmsg || secLabelsErr;
+              }
+            }
+          }
+
+          this.errorModel.clear().set(err);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          return null;
+        }
+      })
+    }),
+    sg = pgBrowser.Nodes['server-group'];
+  }
+
+  return pgBrowser.Nodes['role'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index a337dfe9..ab753f63 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -42,7 +42,7 @@ define('pgadmin.node.server', [
     });
 
     pgAdmin.Browser.Nodes['server'] = pgAdmin.Browser.Node.extend({
-      parent_type: 'server-group',
+      parent_type: 'server_group',
       type: 'server',
       dialogHelp: url_for('help.static', {'filename': 'server_dialog.html'}),
       label: gettext('Server'),
@@ -61,7 +61,7 @@ define('pgadmin.node.server', [
         this.initialized = true;
 
         pgBrowser.add_menus([{
-          name: 'create_server_on_sg', node: 'server-group', module: this,
+          name: 'create_server_on_sg', node: 'server_group', module: this,
           applies: ['object', 'context'], callback: 'show_obj_properties',
           category: 'create', priority: 1, label: gettext('Server...'),
           data: {action: 'create'}, icon: 'wcTabIcon icon-server'
@@ -632,7 +632,7 @@ define('pgadmin.node.server', [
           var isNew = (_.size(attrs) === 0);
 
           if (isNew) {
-            this.set({'gid': args.node_info['server-group']._id});
+            this.set({'gid': args.node_info['server_group']._id});
           }
           pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
         },
@@ -643,7 +643,7 @@ define('pgadmin.node.server', [
           mode: ['properties', 'edit', 'create']
         },{
           id: 'gid', label: gettext('Server group'), type: 'int',
-          control: 'node-list-by-id', node: 'server-group',
+          control: 'node-list-by-id', node: 'server_group',
           mode: ['create', 'edit'], select2: {allowClear: false}
         },{
           id: 'server_type', label: gettext('Server type'), type: 'options',
diff --git a/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py b/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py
index 1535aa4b..dd49045d 100644
--- a/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py
@@ -47,6 +47,14 @@ class TablespaceModule(CollectionNodeModule):
         return servers.ServerModule.NODE_TYPE
 
     @property
+    def module_use_template_javascript(self):
+        """
+        Returns whether Jinja2 template is used for generating the javascript
+        module.
+        """
+        return False
+
+    @property
     def node_inode(self):
         return False
 
diff --git a/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js b/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js
new file mode 100644
index 00000000..61814879
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js
@@ -0,0 +1,406 @@
+define('pgadmin.node.tablespace', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
+  'sources/alerts/alertify_wrapper',
+
+  'pgadmin.browser.collection', 'pgadmin.browser.node.ui',
+  'pgadmin.browser.server.privilege'
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify, AlertifyWrapper) {
+
+  if (!pgBrowser.Nodes['coll-tablespace']) {
+    var databases = pgBrowser.Nodes['coll-tablespace'] =
+      pgBrowser.Collection.extend({
+        node: 'tablespace',
+        label: gettext('Tablespaces'),
+        type: 'coll-tablespace',
+        columns: ['name', 'spcuser', 'description'],
+        hasStatistics: true,
+        statsPrettifyFields: ['Size']
+      });
+  };
+
+  if (!pgBrowser.Nodes['tablespace']) {
+    pgBrowser.Nodes['tablespace'] = pgBrowser.Node.extend({
+      parent_type: 'server',
+      type: 'tablespace',
+      sqlAlterHelp: 'sql-altertablespace.html',
+      sqlCreateHelp: 'sql-createtablespace.html',
+      dialogHelp: url_for('help.static', {'filename': 'tablespace_dialog.html'}),
+      label: gettext('Tablespace'),
+      hasSQL:  true,
+      canDrop: true,
+      hasDepends: true,
+      hasStatistics: true,
+      statsPrettifyFields: ['Size'],
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_tablespace_on_server', node: 'server', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Tablespace...'),
+          icon: 'wcTabIcon icon-tablespace', data: {action: 'create'},
+          enable: 'can_create_tablespace'
+        },{
+          name: 'create_tablespace_on_coll', node: 'coll-tablespace', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Tablespace...'),
+          icon: 'wcTabIcon pg-icon-tablespace', data: {action: 'create'},
+          enable: 'can_create_tablespace'
+        },{
+          name: 'create_tablespace', node: 'tablespace', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: gettext('Tablespace...'),
+          icon: 'wcTabIcon pg-icon-tablespace', data: {action: 'create'},
+          enable: 'can_create_tablespace'
+        },{
+          name: 'move_tablespace', node: 'tablespace', module: this,
+          applies: ['object', 'context'], callback: 'move_objects',
+          category: 'move_tablespace', priority: 5,
+          label: gettext('Move objects to...'),
+          icon: 'fa fa-exchange', data: {action: 'create'},
+          enable: 'can_move_objects'
+        }
+        ]);
+      },
+      can_create_tablespace: function(node, item) {
+        var treeData = this.getTreeNodeHierarchy(item),
+            server = treeData['server'];
+
+        return server.connected && server.user.is_superuser;
+      },
+      can_move_objects: function(node, item) {
+        var treeData = this.getTreeNodeHierarchy(item),
+            server = treeData['server'];
+        // Only supported PG9.4 and above version
+        return server.connected &&
+                server.user.is_superuser &&
+                server.version >= 90400;
+      },
+      callbacks: {
+        /* Move objects from one tablespace to another */
+        move_objects: function(args){
+          var input = args || {},
+            obj = this,
+            t = pgBrowser.tree,
+            i = input.item || t.selected(),
+            d = i && i.length == 1 ? t.itemData(i) : undefined,
+            node = d && pgBrowser.Nodes[d._type],
+            url = obj.generate_url(i, 'move_objects', d, true),
+            msql_url = obj.generate_url(i, 'move_objects_sql', d, true);
+
+          if (!d)
+            return false;
+
+          // Object model
+          var objModel = Backbone.Model.extend({
+              idAttribute: 'id',
+              defaults: {
+                new_tblspc: undefined,
+                obj_type: 'all',
+                user: undefined
+              },
+              schema: [{
+                  id: 'tblspc', label: gettext('New tablespace'),
+                  type: 'text', disabled: false, control: 'node-list-by-name',
+                  node: 'tablespace', select2: {allowClear: false},
+                  filter: function(o) {
+                    return o && (o.label != d.label);
+                  }
+              },{
+                  id: 'obj_type', label: gettext('Object type'),
+                  type: 'text', disabled: false, control: 'select2',
+                  select2: { allowClear: false, width: "100%" },
+                  options: [
+                    {label: "All", value: 'all'},
+                    {label: "Tables", value: 'tables'},
+                    {label: "Indexes", value: 'indexes'},
+                    {label: "Materialized views", value: 'materialized_views'},
+                  ]
+              },{
+                  id: 'user', label: gettext('Object owner'),
+                  type: 'text', disabled: false, control: 'node-list-by-name',
+                  node: 'role', select2: {allowClear: false}
+              },{
+                  id: 'sqltab', label: gettext('SQL'), group: gettext('SQL'),
+                  type: 'text', disabled: false, control: Backform.SqlTabControl.extend({
+                    initialize: function() {
+                      // Initialize parent class
+                      Backform.SqlTabControl.prototype.initialize.apply(this, arguments);
+                    },
+                    onTabChange: function(obj) {
+                      // Fetch the information only if the SQL tab is visible at the moment.
+                      if (this.dialog && obj.shown == this.tabIndex) {
+                            var self = this,
+                            args = self.model.toJSON();
+                            // Add existing tablespace
+                            args.old_tblspc = d.label;
+
+                            // Fetches modified SQL
+                            $.ajax({
+                              url: msql_url,
+                              type: 'GET',
+                              cache: false,
+                              data: args,
+                              dataType: "json",
+                              contentType: "application/json"
+                            }).done(function(res) {
+                              self.sqlCtrl.clearHistory();
+                              self.sqlCtrl.setValue(res.data);
+                              self.sqlCtrl.refresh();
+                            }).fail(function() {
+                              self.model.trigger('pgadmin-view:msql:error');
+                            }).always(function() {
+                              self.model.trigger('pgadmin-view:msql:fetched');
+                            });
+                      }
+                    }
+                  })
+              }],
+              validate: function() {
+                  return null;
+              }
+          });
+
+          if(!alertify.move_objects_dlg) {
+            alertify.dialog('move_objects_dlg' ,function factory() {
+              return {
+                main: function() {
+                 var title = gettext('Move objects to another tablespace ');
+                 this.set('title', title);
+                },
+                build: function() {
+                  alertify.pgDialogBuild.apply(this);
+                },
+                setup:function() {
+                  return {
+                     buttons: [{
+                       text: '', key: 112, className: 'btn btn-default pull-left fa fa-lg fa-question',
+                       attrs:{name:'dialog_help', type:'button', label: gettext('Users'),
+                       url: url_for('help.static', {'filename': 'move_objects.html'})}
+                       },{
+                       text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button'
+                       },{
+                       text: gettext('Cancel'), key: 27, className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button'
+                     }],
+                     // Set options for dialog
+                     options: {
+                       //disable both padding and overflow control.
+                       padding : !1,
+                       overflow: !1,
+                       modal: false,
+                       resizable: true,
+                       maximizable: true,
+                       pinnable: false,
+                       closableByDimmer: false
+                     }
+                   };
+                },
+                hooks: {
+                  // Triggered when the dialog is closed
+                  onclose: function() {
+                    if (this.view) {
+                      // clear our backform model/view
+                      this.view.remove({data: true, internal: true, silent: true});
+                    }
+                  }
+                },
+                prepare: function() {
+                  var self = this,
+                    $container = $("<div class='move_objects'></div>");
+                  //Disbale Okay button
+                  this.__internal.buttons[1].element.disabled = true;
+                  // Find current/selected node
+                  var t = pgBrowser.tree,
+                    i = t.selected(),
+                    d = i && i.length == 1 ? t.itemData(i) : undefined,
+                    node = d && pgBrowser.Nodes[d._type];
+
+                  if (!d)
+                    return;
+                  // Create treeInfo
+                  var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+                  // Instance of backbone model
+                  var newModel = new objModel({}, {node_info: treeInfo}),
+                      fields = Backform.generateViewSchema(
+                        treeInfo, newModel, 'create', node,
+                        treeInfo.server, true
+                      );
+
+                  var view = this.view = new Backform.Dialog({
+                    el: $container, model: newModel, schema: fields
+                  });
+                  // Add our class to alertify
+                  $(this.elements.body.childNodes[0]).addClass(
+                    'alertify_tools_dialog_properties obj_properties'
+                  );
+                  // Render dialog
+                  view.render();
+
+                  this.elements.content.appendChild($container.get(0));
+
+                  // Listen to model & if filename is provided then enable Backup button
+                  this.view.model.on('change', function() {
+                    if (!_.isUndefined(this.get('tblspc')) && this.get('tblspc') !== '') {
+                      this.errorModel.clear();
+                      self.__internal.buttons[1].element.disabled = false;
+                    } else {
+                      self.__internal.buttons[1].element.disabled = true;
+                      this.errorModel.set('tblspc', gettext('Please select tablespace'))
+                    }
+                  });
+                },
+                // Callback functions when click on the buttons of the Alertify dialogs
+                callback: function(e) {
+                  if (e.button.element.name == "dialog_help") {
+                    e.cancel = true;
+                    pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                      null, null, e.button.element.getAttribute('label'));
+                    return;
+                  }
+                  if (e.button.text === gettext('OK')) {
+                    var self = this,
+                        args =  this.view.model.toJSON();
+                        args.old_tblspc = d.label;
+                    e.cancel = true;
+                    alertify.confirm(
+                      gettext('Move objects...'),
+                      gettext(
+                        'Are you sure you wish to move objects %(old_tablespace)s to %s(new_tablespace)s',
+                        {old_tablespace: args.old_tblspc, new_tablespace: args.tblspc}
+                      ),
+                      function() {
+                        $.ajax({
+                          url: url,
+                          method:'PUT',
+                          data:{'data': JSON.stringify(args) },
+                          success: function(res) {
+                            if (res.success) {
+                              var alertifyWrapper = new AlertifyWrapper();
+                              alertifyWrapper.success(res.info);
+                              self.close();
+                            } else {
+                              var alertifyWrapper = new AlertifyWrapper();
+                              alertifyWrapper.error(res.errormsg);
+                            }
+                          },
+                          error: function(xhr, status, error) {
+                            try {
+                              var err = $.parseJSON(xhr.responseText);
+                              if (err.success == 0) {
+                                var alertifyWrapper = new AlertifyWrapper();
+                                alertifyWrapper.error(err.errormsg);
+                              }
+                            } catch (e) {}
+                          }
+                        });
+                      },
+                      function() {
+                        // Do nothing as user cancel the operation
+                      }
+                    );
+                  }
+                }
+              }
+            });
+          }
+          alertify.move_objects_dlg(true).resizeTo('40%','50%');;
+        }
+      },
+      model: pgBrowser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          owner: undefined,
+          comment: undefined,
+          spclocation: undefined,
+          spcoptions: [],
+          spcacl: [],
+          seclabels:[]
+        },
+
+        // Default values!
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+
+          if (isNew) {
+            var 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: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'text', disabled: true, mode: ['properties']
+        },{
+          id: 'spclocation', label: gettext('Location'), cell: 'string',
+          group: gettext('Definition'), type: 'text', mode: ['properties', 'edit','create'],
+          disabled: function(m) {
+            // To disabled it in edit mode,
+            // We'll check if model is new if yes then disabled it
+            return !m.isNew();
+          }
+        },{
+          id: 'spcuser', label: gettext('Owner'), cell: 'string',
+          type: 'text', control: 'node-list-by-name', node: 'role',
+          select2: {allowClear: false}
+        },{
+          id: 'acl', label: gettext('Privileges'), type: 'text',
+          group: gettext('Security'), mode: ['properties'], disabled: true
+        },{
+          id: 'description', label: gettext('Comment'), cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'spcoptions', label: gettext('Parameters'), type: 'collection',
+          group: gettext("Parameters"), control: 'variable-collection',
+          model: pgBrowser.Node.VariableModel,
+          mode: ['edit', 'create'], canAdd: true, canEdit: false,
+          canDelete: true
+         },{
+          id: 'spcacl', label: gettext('Privileges'), type: 'collection',
+          group: gettext('Security'), control: 'unique-col-collection',
+          model: pgBrowser.Node.PrivilegeRoleModel.extend({privileges: ['C']}),
+          mode: ['edit', 'create'], canAdd: true, canDelete: true,
+          uniqueCol : ['grantee'],
+          columns: ['grantee', 'grantor', 'privileges']
+         },{
+          id: 'seclabels', label: gettext('Security Labels'),
+          model: pgBrowser.SecLabelModel, editable: false, type: 'collection',
+          group: gettext('Security'), mode: ['edit', 'create'],
+          min_version: 90200, canAdd: true,
+          canEdit: false, canDelete: true, control: 'unique-col-collection'
+        }
+        ],
+        validate: function() {
+          var err = {},
+            errmsg = null,
+            changedAttrs = this.sessAttrs,
+            msg = undefined;
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', msg);
+          } else if (_.isUndefined(this.get('spclocation'))
+              || String(this.get('spclocation')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Location cannot be empty.');
+            this.errorModel.set('spclocation', msg);
+          } else {
+            this.errorModel.unset('name');
+            this.errorModel.unset('spclocation');
+          }
+          return null;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['coll-tablespace'];
+});
diff --git a/web/pgadmin/browser/server_groups/static/img/server_group.png b/web/pgadmin/browser/server_groups/static/img/server_group.png
new file mode 100644
index 00000000..bd49bd27
Binary files /dev/null and b/web/pgadmin/browser/server_groups/static/img/server_group.png differ
diff --git a/web/pgadmin/browser/server_groups/static/js/server_group.js b/web/pgadmin/browser/server_groups/static/js/server_group.js
new file mode 100644
index 00000000..d0990842
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/static/js/server_group.js
@@ -0,0 +1,82 @@
+define('pgadmin.node.server_group', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin',
+  'backbone', 'pgadmin.browser', 'pgadmin.browser.node'
+], function(gettext, url_for, $, _, pgAdmin, Backbone) {
+
+  if (!pgAdmin.Browser.Nodes['server_group']) {
+    pgAdmin.Browser.Nodes['server_group'] = pgAdmin.Browser.Node.extend({
+      parent_type: null,
+      type: 'server_group',
+      dialogHelp: url_for('help.static', {'filename': 'server_group_dialog.html'}),
+      label: gettext('Server Group'),
+      width: '350px',
+      height: '250px',
+      is_collection: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgAdmin.Browser.add_menus([{
+          name: 'create_server_group', node: 'server_group', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 1, label: gettext('Server Group...'),
+          data: {'action': 'create'}, icon: 'wcTabIcon icon-server_group'
+        }]);
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          id: undefined,
+          name: null
+        },
+        schema: [
+          {
+            id: 'id', label: gettext('ID'), type: 'int', group: null,
+            mode: ['properties']
+          },{
+            id: 'name', label: gettext('Name'), type: 'text', group: null,
+            mode: ['properties', 'edit', 'create']
+          }
+        ],
+        validate: function(attrs, options) {
+           var err = {},
+              errmsg = null;
+          this.errorModel.clear();
+
+          if (!this.isNew() && 'id' in this.changed) {
+            errmsg = gettext('The ID cannot be changed.');
+            this.errorModel.set('id', errmsg);
+            return errmsg;
+          }
+          if (_.isUndefined(this.get('name')) ||
+            _.isNull(this.get('name')) ||
+            String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            errmsg = gettext('Name cannot be empty.');
+            this.errorModel.set('name', errmsg);
+            return errmsg;
+          }
+          return null;
+        }
+      }),
+      canDrop: function(itemData, item, data) {
+        if(itemData.can_delete) {
+          return true;
+        }
+        return false;
+      },
+      canDelete: function(i) {
+        var s = pgAdmin.Browser.tree.siblings(i, true);
+
+        /* This is the only server group - we can't remove it*/
+        if (!s || s.length == 0) {
+          return false;
+        }
+        return true;
+      }
+    });
+  }
+
+  return pgAdmin.Browser.Nodes['server_group'];
+});
diff --git a/web/pgadmin/browser/server_groups/tests/test_sg_get.py b/web/pgadmin/browser/server_groups/tests/test_sg_get.py
index 7b3df43f..352476b2 100644
--- a/web/pgadmin/browser/server_groups/tests/test_sg_get.py
+++ b/web/pgadmin/browser/server_groups/tests/test_sg_get.py
@@ -20,7 +20,7 @@ class SgNodeTestCase(BaseTestGenerator):
 
     scenarios = [
         # Fetching the default url for server group node
-        ('Check Server Group Node', dict(url='/browser/server-group/obj/'))
+        ('Check Server Group Node', dict(url='/browser/server_group/obj/'))
     ]
 
     def runTest(self):
diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
new file mode 100644
index 00000000..aa6442e3
--- /dev/null
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -0,0 +1,2004 @@
+define(
+  'pgadmin.browser', [
+    'sources/gettext', 'sources/url_for', 'require', 'jquery', 'underscore', 'underscore.string',
+    'bootstrap', 'pgadmin', 'alertify', 'bundled_codemirror',
+    'sources/check_node_visibility', 'pgadmin.browser.utils', 'wcdocker',
+    'jquery.contextmenu', 'jquery.aciplugin', 'jquery.acitree',
+    'pgadmin.alertifyjs', 'pgadmin.browser.messages',
+    'pgadmin.browser.menu', 'pgadmin.browser.panel',
+    'pgadmin.browser.error', 'pgadmin.browser.frame',
+    'pgadmin.browser.node', 'pgadmin.browser.collection'
+  ], function(
+    gettext, url_for, require, $, _, S, Bootstrap, pgAdmin, Alertify,
+    codemirror, checkNodeVisibility
+) {
+  window.jQuery = window.$ = $;
+  // Some scripts do export their object in the window only.
+  // Generally the one, which do no have AMD support.
+  var wcDocker = window.wcDocker;
+  $ = $ || window.jQuery || window.$;
+  Bootstrap = Bootstrap || window.Bootstrap;
+  var CodeMirror = codemirror.default;
+
+  var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
+  var select_object_msg = gettext('Please select an object in the tree view.');
+
+  var panelEvents = {};
+  panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
+    if (this.isVisible()) {
+      var obj = pgAdmin.Browser,
+          i   = obj.tree ? obj.tree.selected() : undefined,
+          d   = i && i.length == 1 ? obj.tree.itemData(i) : undefined;
+
+      if (d && obj.Nodes[d._type].callbacks['selected'] &&
+          _.isFunction(obj.Nodes[d._type].callbacks['selected'])) {
+        return obj.Nodes[d._type].callbacks['selected'].apply(
+            obj.Nodes[d._type], [i, d, obj]);
+      }
+    }
+  };
+
+  var processTreeData = function(payload) {
+    var data = JSON.parse(payload).data;
+    if (data.length && data[0]._type !== 'column' &&
+        data[0]._type !== 'catalog_object_column') {
+      data = data.sort(function(a, b) {
+          return pgAdmin.natural_sort(a.label, b.label);
+      });
+    }
+    _.each(data, function(d){
+      d._label = d.label;
+      d.label = _.escape(d.label);
+    })
+    return data;
+  };
+
+  var initializeBrowserTree = pgAdmin.Browser.initializeBrowserTree =
+    function(b) {
+      $('#tree').aciTree({
+        ajax: {
+          url: url_for('browser.nodes'),
+          converters: {
+            'text json': processTreeData,
+          }
+        },
+        ajaxHook: function(item, settings) {
+          if (item != null) {
+            var d = this.itemData(item);
+            var n = b.Nodes[d._type];
+            if (n)
+              settings.url = n.generate_url(item, 'children', d, true);
+          }
+        },
+        loaderDelay: 100,
+        show: {
+          duration: 75
+        },
+        hide: {
+          duration: 75
+        },
+        view: {
+          duration: 75
+        }
+      });
+
+      b.tree = $('#tree').aciTree('api');
+    };
+
+  // Extend the browser class attributes
+  _.extend(pgAdmin.Browser, {
+    // The base url for browser
+    URL: url_for('browser.index'),
+    // We do have docker of type wcDocker to take care of different
+    // containers. (i.e. panels, tabs, frames, etc.)
+    docker:null,
+    // Reversed Engineer query for the selected database node object goes
+    // here
+    editor:null,
+    // Left hand browser tree
+    tree:null,
+    // list of script to be loaded, when a certain type of node is loaded
+    // It will be used to register extensions, tools, child node scripts,
+    // etc.
+    scripts: {},
+    // Default panels
+    panels: {
+      // Panel to keep the left hand browser tree
+      'browser': new pgAdmin.Browser.Panel({
+        name: 'browser',
+        title: gettext('Browser'),
+        showTitle: true,
+        isCloseable: false,
+        isPrivate: true,
+        icon: 'fa fa-binoculars',
+        content: '<div id="tree" class="aciTree"></div>'
+      }),
+      // Properties of the object node
+      'properties': new pgAdmin.Browser.Panel({
+        name: 'properties',
+        title: gettext('Properties'),
+        icon: 'fa fa-cogs',
+        width: 500,
+        isCloseable: false,
+        isPrivate: true,
+        elContainer: true,
+        content: '<div class="obj_properties"><div class="alert alert-info pg-panel-message">' + select_object_msg + '</div></div>',
+        events: panelEvents,
+        onCreate: function(myPanel, $container) {
+          $container.addClass('pg-no-overflow');
+        }
+      }),
+      // Statistics of the object
+      'statistics': new pgAdmin.Browser.Panel({
+        name: 'statistics',
+        title: gettext('Statistics'),
+        icon: 'fa fa-line-chart',
+        width: 500,
+        isCloseable: false,
+        isPrivate: true,
+        content: '<div><div class="alert alert-info pg-panel-message pg-panel-statistics-message">' + select_object_msg + '</div><div class="pg-panel-statistics-container hidden"></div></div>',
+        events: panelEvents
+      }),
+      // Reversed engineered SQL for the object
+      'sql': new pgAdmin.Browser.Panel({
+        name: 'sql',
+        title: gettext('SQL'),
+        icon: 'fa fa-file-text-o',
+        width: 500,
+        isCloseable: false,
+        isPrivate: true,
+        content: '<textarea id="sql-textarea" name="sql-textarea"></textarea>'
+      }),
+      // Dependencies of the object
+      'dependencies': new pgAdmin.Browser.Panel({
+        name: 'dependencies',
+        title: gettext('Dependencies'),
+        icon: 'fa fa-hand-o-up',
+        width: 500,
+        isCloseable: false,
+        isPrivate: true,
+        content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container hidden"></div></div>',
+        events: panelEvents
+      }),
+      // Dependents of the object
+      'dependents': new pgAdmin.Browser.Panel({
+        name: 'dependents',
+        title: gettext('Dependents'),
+        icon: 'fa fa-hand-o-down',
+        width: 500,
+        isCloseable: false,
+        isPrivate: true,
+        content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container hidden"></div></div>',
+        events: panelEvents
+      })
+    },
+    // We also support showing dashboards, HTML file, external URL
+    frames: {},
+      /* Menus */
+      // pgAdmin.Browser.MenuItem.add_menus(...) will register all the menus
+      // in this container
+    menus: {
+      // All context menu goes here under certain menu types.
+      // i.e. context: {'server': [...], 'server-group': [...]}
+      context: {},
+      // File menus
+      file: {},
+      // Edit menus
+      edit: {},
+      // Object menus
+      object: {},
+      // Management menus
+      management: {},
+      // Tools menus
+      tools: {},
+      // Help menus
+      help: {}
+    },
+    add_panels: function() {
+      /* Add hooked-in panels by extensions */
+      //debugger;
+      var panels = JSON.parse(pgBrowser.panels_items);
+      _.each(panels, function(panel) {
+        if (panel.isIframe) {
+          pgBrowser.frames[panel.name] = new pgBrowser.Frame({
+            name: panel.name,
+            title: panel.title,
+            icon: panel.icon,
+            width: panel.width,
+            height: panel.height,
+            showTitle: panel.showTitle,
+            isCloseable: panel.isCloseable,
+            isPrivate: panel.isPrivate,
+            url: panel.content
+          })
+        } else {
+          pgBrowser.panels[panel.name] = new pgBrowser.Panel({
+            name: panel.name,
+            title: panel.title,
+            icon: panel.icon,
+            width: panel.width,
+            height: panel.height,
+            showTitle: panel.showTitle,
+            isCloseable: panel.isCloseable,
+            isPrivate: panel.isPrivate,
+            content: (panel.content) ? panel.content : '',
+            events: (panel.events) ? panel.events : ''
+          })
+        }
+      });
+
+    },
+    menu_categories: {
+      /* name, label (pair) */
+      'create': {
+        label: gettext('Create'),
+        priority: 1,
+        /* separator above this menu */
+        above: false,
+        below: true,
+        icon: 'fa fa-magic',
+        single: true
+      }
+    },
+    // A callback to load/fetch a script when a certain node is loaded
+    register_script: function(n, m, p) {
+      var scripts = this.scripts;
+      scripts[n] = _.isArray(scripts[n]) ? scripts[n] : [];
+      scripts[n].push({'name': m, 'path': p, loaded: false});
+    },
+    // Build the default layout
+    buildDefaultLayout: function() {
+      var browserPanel = this.docker.addPanel('browser', wcDocker.DOCK.LEFT);
+      var dashboardPanel = this.docker.addPanel(
+              'dashboard', wcDocker.DOCK.RIGHT, browserPanel);
+      this.docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, {
+          tabOrientation: wcDocker.TAB.TOP
+      });
+      this.docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel);
+      this.docker.addPanel(
+              'statistics', wcDocker.DOCK.STACKED, dashboardPanel);
+      this.docker.addPanel(
+              'dependencies', wcDocker.DOCK.STACKED, dashboardPanel);
+      this.docker.addPanel(
+              'dependents', wcDocker.DOCK.STACKED, dashboardPanel);
+    },
+    // Enable/disable menu options
+    enable_disable_menus: function(item) {
+      // Mechanism to enable/disable menus depending on the condition.
+      var obj = this, j, e,
+          // menu navigation bar
+          navbar = $('#navbar-menu > ul').first(),
+          // Drop down menu for objects
+          $obj_mnu = navbar.find('li#mnu_obj > ul.dropdown-menu').first(),
+          // data for current selected object
+          d = obj.tree.itemData(item),
+          update_menuitem = function(m) {
+            if (m instanceof pgAdmin.Browser.MenuItem) {
+              m.update(d, item);
+            } else {
+              for (var key in m) {
+                update_menuitem(m[key]);
+              }
+            }
+          };
+
+      // All menus from the object menus (except the create drop-down
+      // menu) needs to be removed.
+      $obj_mnu.empty();
+
+      // All menus (except for the object menus) are already present.
+      // They will just require to check, wheather they are
+      // enabled/disabled.
+      _.each([
+        {m: 'file', id: '#mnu_file'},
+        {m: 'edit', id: '#mnu_edit'},
+        {m: 'management', id: '#mnu_management'},
+        {m: 'tools', id: '#mnu_tools'},
+        {m: 'help', id:'#mnu_help'}], function(o) {
+          _.each(
+            obj.menus[o.m],
+            function(m, k) {
+              update_menuitem(m);
+            });
+        });
+
+      // Create the object menu dynamically
+      if (item && obj.menus['object'] && obj.menus['object'][d._type]) {
+        pgAdmin.Browser.MenuCreator(
+          $obj_mnu, obj.menus['object'][d._type], obj.menu_categories, d, item
+        )
+      } else {
+        // Create a dummy 'no object seleted' menu
+        var create_submenu = pgAdmin.Browser.MenuGroup(
+          obj.menu_categories['create'], [{
+            $el: $('<li class="menu-item disabled"><a href="#">' + gettext("No object selected") + '</a></li>'),
+            priority: 1,
+            category: 'create',
+            update: function() {}
+          }], false);
+        $obj_mnu.append(create_submenu.$el);
+      }
+    },
+    save_current_layout: function(obj) {
+      if(obj.docker) {
+        var state = obj.docker.save();
+        var settings = { setting: "Browser/Layout", value: state };
+        $.ajax({
+          type: 'POST',
+          url: url_for('settings.store_bulk'),
+          data: settings
+        });
+      }
+    },
+    init: function() {
+      var obj=this;
+      if (obj.initialized) {
+        return;
+      }
+      obj.initialized = true;
+
+      // Cache preferences
+      obj.cache_preferences();
+      this.add_panels();
+      // Initialize the Docker
+      obj.docker = new wcDocker(
+        '#dockerContainer', {
+        allowContextMenu: true,
+        allowCollapse: false,
+        themePath: '../static/css/',
+        theme: 'webcabin.overrides.css'
+      });
+      if (obj.docker) {
+        // Initialize all the panels
+        _.each(obj.panels, function(panel, name) {
+          obj.panels[name].load(obj.docker);
+        });
+        // Initialize all the frames
+        _.each(obj.frames, function(frame, name) {
+          obj.frames[name].load(obj.docker);
+        });
+
+        // Stored layout in database from the previous session
+        var layout = pgBrowser.utils.layout;
+
+        // Try to restore the layout if there is one
+        if (layout != '') {
+          try {
+            obj.docker.restore(layout)
+          }
+          catch(err) {
+            obj.docker.clear();
+            obj.buildDefaultLayout()
+          }
+        } else {
+          obj.buildDefaultLayout()
+        }
+
+        // Listen to panel attach/detach event so that last layout will be remembered
+        _.each(obj.panels, function(panel, name) {
+          if (panel.panel) {
+            panel.panel.on(wcDocker.EVENT.ATTACHED, function() {
+              obj.save_current_layout(obj);
+            });
+            panel.panel.on(wcDocker.EVENT.DETACHED, function() {
+              obj.save_current_layout(obj);
+            });
+            panel.panel.on(wcDocker.EVENT.MOVE_ENDED, function() {
+              obj.save_current_layout(obj);
+            });
+          }
+        });
+      }
+
+      // Syntax highlight the SQL Pane
+      obj.editor = CodeMirror.fromTextArea(
+          document.getElementById("sql-textarea"), {
+            lineNumbers: true,
+            mode: "text/x-pgsql",
+            readOnly: true,
+            extraKeys: pgAdmin.Browser.editor_shortcut_keys,
+            tabSize: pgAdmin.Browser.editor_options.tabSize,
+            lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
+            autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
+            matchBrackets: pgAdmin.Browser.editor_options.brace_matching
+          });
+
+      setTimeout(function() {
+        obj.editor.refresh();
+      }, 10);
+
+      // Initialise the treeview
+      initializeBrowserTree(obj);
+
+      // Build the treeview context menu
+      $('#tree').contextMenu({
+        selector: '.aciTreeLine',
+        autoHide: false,
+        build: function(element) {
+          var item = obj.tree.itemFrom(element),
+              d = obj.tree.itemData(item),
+              menus = obj.menus['context'][d._type],
+              $div = $('<div></div>'),
+              context_menu = {};
+
+          pgAdmin.Browser.MenuCreator(
+            $div, menus, obj.menu_categories, d, item, context_menu
+          );
+
+          return {
+            autoHide: false,
+            items: context_menu
+          };
+        }
+      });
+
+      // Treeview event handler
+      $('#tree').on('acitree', function(event, api, item, eventName, options) {
+        var d = item ? obj.tree.itemData(item) : null;
+
+        switch (eventName) {
+          // When a node is added in the browser tree, we need to
+          // load the registered scripts
+          case "added":
+            if (d) {
+              /* Loading all the scripts registered to be loaded on this node */
+              if (obj.scripts && obj.scripts[d._type]) {
+                var scripts = _.extend({}, obj.scripts[d._type]);
+
+                /*
+                 * We can remove it from the Browser.scripts object as
+                 * these're about to be loaded.
+                 *
+                 * This will make sure that we check for the script to be
+                 * loaded only once.
+                 *
+                 */
+                delete obj.scripts[d._type];
+              }
+            }
+            break;
+        }
+
+        var node;
+
+        if (d && obj.Nodes[d._type]) {
+          node = obj.Nodes[d._type];
+
+          /* If the node specific callback returns false, we will also return
+           * false for further processing.
+           */
+          if (_.isObject(node.callbacks) &&
+              eventName in node.callbacks &&
+              typeof node.callbacks[eventName] == 'function' &&
+              !node.callbacks[eventName].apply(
+                  node, [item, d, obj, options, eventName])) {
+            return false;
+          }
+          /* Raise tree events for the nodes */
+          try {
+            node.trigger(
+                'browser-node.' + eventName, node, item, d
+                );
+          } catch (e) {
+            console.log(e);
+          }
+        }
+
+        try {
+          obj.Events.trigger(
+              'pgadmin-browser:tree', eventName, item, d
+              );
+          obj.Events.trigger(
+              'pgadmin-browser:tree:' + eventName, item, d, node
+              );
+        } catch (e) {
+          console.log(e);
+        }
+        return true;
+      });
+
+      // Register scripts and add menus
+      pgBrowser.utils.registerScripts(this);
+      pgBrowser.utils.addMenus(obj);
+
+      // Ping the server every 5 minutes
+      setInterval(function() {
+        $.ajax({
+          url: url_for('misc.ping'),
+          type:'POST',
+          success: function() {},
+          error: function() {}
+          });
+        }, 300000);
+      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.onRefreshTreeNode, obj);
+    },
+
+    add_menu_category: function(
+      id, label, priority, icon, above_separator, below_separator, single
+    ) {
+      this.menu_categories[id] = {
+        label: label,
+        priority: priority,
+        icon: icon,
+        above: (above_separator === true),
+        below: (below_separator === true),
+        single: single
+      }
+    },
+
+    // This will hold preference data (Works as a cache object)
+    // Here node will be a key and it's preference data will be value
+    preferences_cache: {},
+
+    // Add menus of module/extension at appropriate menu
+    add_menus: function(menus) {
+      var self = this,
+        pgMenu = this.menus,
+        MenuItem = pgAdmin.Browser.MenuItem;
+      _.each(menus, function(m) {
+        _.each(m.applies, function(a) {
+          /* We do support menu type only from this list */
+          if ($.inArray(a, [
+              'context', 'file', 'edit', 'object',
+              'management', 'tools', 'help']) >= 0) {
+            var menus;
+
+            // If current node is not visible in browser tree
+            // then return from here
+            if(!checkNodeVisibility(self, m.node)) {
+                return;
+            } else if(_.has(m, 'module') && !_.isUndefined(m.module)) {
+              // If module to which this menu applies is not visible in
+              // browser tree then also we do not display menu
+              if(!checkNodeVisibility(self, m.module.type)) {
+                return;
+              }
+            }
+
+            pgMenu[a] = pgMenu[a] || {};
+            if (_.isString(m.node)) {
+              menus = pgMenu[a][m.node] = pgMenu[a][m.node] || {};
+            } else if (_.isString(m.category)) {
+              menus = pgMenu[a][m.category] = pgMenu[a][m.category] || {};
+            }
+            else {
+              menus = pgMenu[a];
+            }
+
+            if (!_.has(menus, m.name)) {
+              menus[m.name] = new MenuItem({
+                name: m.name, label: m.label, module: m.module,
+                category: m.category, callback: m.callback,
+                priority: m.priority, data: m.data, url: m.url,
+                target: m.target, icon: m.icon,
+                enable: (m.enable == '' ? true : (_.isString(m.enable) &&
+                   m.enable.toLowerCase() == 'false') ?
+                  false : m.enable),
+                node: m.node
+              });
+            }
+          } else  {
+            console && console.log &&
+              console.log(
+                  "Developer warning: Category '" +
+                  a +
+                  "' is not supported!\nSupported categories are: context, file, edit, object, tools, management, help");
+          }
+        });
+      });
+    },
+    // Create the menus
+    create_menus: function() {
+
+      /* Create menus */
+      var navbar = $('#navbar-menu > ul').first();
+      var obj = this;
+
+      _.each([
+        {menu: 'file', id: '#mnu_file'},
+        {menu: 'edit', id: '#mnu_edit'},
+        {menu: 'management', id: '#mnu_management'},
+        {menu: 'tools', id: '#mnu_tools'},
+        {menu: 'help', id:'#mnu_help'}],
+        function(o) {
+          var $mnu = navbar.children(o.id).first(),
+              $dropdown = $mnu.children('.dropdown-menu').first();
+          $dropdown.empty();
+          var menus = {};
+
+          if (pgAdmin.Browser.MenuCreator(
+            $dropdown, obj.menus[o.menu], obj.menu_categories
+            )) {
+            $mnu.removeClass('hide');
+          }
+        });
+
+      navbar.children('#mnu_obj').removeClass('hide');
+       obj.enable_disable_menus();
+    },
+    // General function to handle callbacks for object or dialog help.
+    showHelp: function(type, url, node, item, label) {
+      if (type == "object_help") {
+        // See if we can find an existing panel, if not, create one
+        var pnlSqlHelp = this.docker.findPanels('pnl_sql_help')[0];
+
+        if (pnlSqlHelp == null) {
+          var pnlProperties = this.docker.findPanels('properties')[0];
+          this.docker.addPanel('pnl_sql_help', wcDocker.DOCK.STACKED, pnlProperties);
+          pnlSqlHelp = this.docker.findPanels('pnl_sql_help')[0];
+        }
+
+        // Construct the URL
+        var server = node.getTreeNodeHierarchy(item).server;
+        var baseUrl = pgBrowser.utils.pg_help_path;
+        if (server.server_type == 'ppas') {
+          baseUrl = pgBrowser.utils.edbas_help_path;
+        }
+
+        var major = Math.floor(server.version / 10000),
+          minor = Math.floor(server.version / 100) - (major * 100);
+
+        baseUrl = baseUrl.replace('$VERSION$', major + '.' + minor)
+        if (!S(baseUrl).endsWith('/')) {
+          baseUrl = baseUrl + '/'
+        }
+        var fullUrl = baseUrl+ url;
+        // Update the panel
+        var iframe = $(pnlSqlHelp).data('embeddedFrame');
+        pnlSqlHelp.title('Help: '+ label);
+
+        pnlSqlHelp.focus();
+        iframe.openURL(fullUrl);
+      } else if(type == "dialog_help") {
+        if(this.docker) {
+          // See if we can find an existing panel, if not, create one
+          var pnlDialogHelp = this.docker.findPanels('pnl_online_help')[0];
+
+          if (pnlDialogHelp == null) {
+            var pnlProperties = this.docker.findPanels('properties')[0];
+            this.docker.addPanel('pnl_online_help', wcDocker.DOCK.STACKED, pnlProperties);
+            pnlDialogHelp = this.docker.findPanels('pnl_online_help')[0];
+          }
+
+          // Update the panel
+          var iframe = $(pnlDialogHelp).data('embeddedFrame');
+
+          pnlDialogHelp.focus();
+          iframe.openURL(url);
+        } else {
+          // We have added new functionality of opening Query tool & debugger in new
+          // browser tab, In that case we will not have docker object available
+          // so we will open dialog help in new browser tab
+          window.open(url, '_blank');
+        }
+      }
+    },
+
+    // Get preference value from cache
+    get_preference: function(module, preference) {
+      var self = this;
+      // If cache is not yet loaded then keep checking
+      if(_.size(self.preferences_cache) == 0) {
+        var preference_data = setInterval(check_preference, 1000);
+
+        function check_preference() {
+          if(_.size(self.preferences_cache) > 0) {
+            clearInterval(preference_data);
+            return _.findWhere(self.preferences_cache, {'module': module, 'name': preference});
+          }
+        }
+      }
+      else {
+        return _.findWhere(self.preferences_cache, {'module': module, 'name': preference});
+      }
+    },
+
+    // Get and cache the preferences
+    cache_preferences: function () {
+      var self = this;
+      $.ajax({
+        url: url_for('preferences.get_all'),
+        success: function(res) {
+          self.preferences_cache = res;
+        },
+        error: function(xhr, status, error) {
+          try {
+            var err = $.parseJSON(xhr.responseText);
+            Alertify.alert(gettext('Preference loading failed.'),
+              err.errormsg
+            );
+          } catch (e) {}
+        }
+      });
+    },
+
+    _findTreeChildNode: function(_i, _d, _o) {
+      var loaded = _o.t.wasLoad(_i),
+          done = true,
+          onLoad = function() {
+            var items = _o.t.children(_i),
+                i, d, n, idx = 0, size = items.length;
+            for (; idx < size; idx++) {
+              i = items.eq(idx);
+              d = _o.t.itemData(i);
+              if (d._type === _d._type) {
+                if (!_o.hasId || d._id == _d._id) {
+                  _o.i = i;
+                  _o.d = _d;
+                  _o.pI.push({coll: false, item: i, d: _d});
+
+                  _o.success();
+                  return;
+                }
+              } else {
+                n = _o.b.Nodes[d._type];
+                // Are we looking at the collection node for the given node?
+                if (
+                  n && n.collection_node && d.nodes &&
+                  _.indexOf(d.nodes, _d._type) != -1
+                ) {
+                  _o.i = i;
+                  _o.d = null;
+                  _o.pI.push({coll: true, item: i, d: d});
+
+                  // Set load to false when the current collection node's inode is false
+                  if (!_o.t.isInode(i)) {
+                    _o.load = false;
+                  }
+                  _o.b._findTreeChildNode(i, _d, _o);
+                  return;
+                }
+              }
+            }
+            _o.notFound && typeof(_o.notFound) == 'function' &&
+              _o.notFound(_d);
+          };
+
+      if (!loaded && _o.load) {
+        _o.t.open(_i, {
+          success: onLoad,
+          unanimated: true,
+          fail: function() {
+            var fail = _o && _o.o && _o.o.fail;
+
+            if (
+              fail && typeof(fail) == 'function'
+            ) {
+              fail.apply(_o.t, []);
+            }
+          }
+        });
+      } else if (loaded) {
+        onLoad();
+      } else {
+        _o.notFound && typeof(_o.notFound) == 'function' &&
+          _o.notFound(_d);
+      }
+
+      return;
+    },
+
+    onAddTreeNode: function(_data, _hierarchy, _opts) {
+      var ctx = {
+            b: this, // Browser
+            d: null, // current parent
+            hasId: true,
+            i: null, // current item
+            p: _.toArray(_hierarchy || {}).sort(
+              function(a, b) {
+                return (a.priority === b.priority) ? 0 : (
+                  a.priority < b.priority ? -1 : 1
+                );
+              }
+            ), // path of the parent
+            pI: [], // path Item
+            t: this.tree, // Tree Api
+            o: _opts
+          },
+          traversePath = function() {
+            var ctx = this, i, d;
+
+            ctx.success = traversePath;
+            if (ctx.p.length) {
+              d = ctx.p.shift();
+              // This is the parent node.
+              // Replace the parent-id of the data, which could be different
+              // from the given hierarchy.
+              if (!ctx.p.length) {
+                d._id = _data._pid;
+                ctx.success = addItemNode;
+              }
+              ctx.b._findTreeChildNode(
+                ctx.i, d, ctx
+              );
+              // if parent node is null
+              if (!_data._pid) {
+                addItemNode.apply(ctx, arguments);
+              }
+            }
+            return true;
+          }.bind(ctx),
+          addItemNode = function() {
+            // Append the new data in the tree under the current item.
+            // We may need to pass it to proper collection node.
+            var ctx = this,
+                first = (ctx.i || this.t.wasLoad(ctx.i)) &&
+                  this.t.first(ctx.i),
+                findChildNode = function(success, notFound) {
+                  var ctx = this;
+                  ctx.success = success;
+                  ctx.notFound = notFound;
+
+                  ctx.b._findTreeChildNode(ctx.i, _data, ctx);
+                }.bind(ctx),
+                selectNode = function() {
+                  this.t.openPath(this.i);
+                  this.t.select(this.i);
+                  if (
+                    ctx.o && ctx.o.success && typeof(ctx.o.success) == 'function'
+                  ) {
+                    ctx.o.success.apply(ctx.t, [ctx.i, _data]);
+                  }
+                }.bind(ctx),
+                addNode = function() {
+                  var ctx = this,
+                      items = ctx.t.children(ctx.i),
+                      s = 0, e = items.length - 1, i,
+                      linearSearch = function() {
+                        while (e >= s) {
+                          i = items.eq(s);
+                          var d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _data._label
+                            ) == 1
+                          )
+                            return true;
+                          s++;
+                        }
+                        if (e != items.length - 1) {
+                          i = items.eq(e);
+                          return true;
+                        }
+                        i = null;
+                        return false;
+                      },
+                      binarySearch = function() {
+                        var d, m;
+                        // Binary search only outperforms Linear search for n > 44.
+                        // Reference:
+                        // https://en.wikipedia.org/wiki/Binary_search_algorithm#cite_note-30
+                        //
+                        // We will try until it's half.
+                        while (e - s > 22) {
+                          i = items.eq(s);
+                          var d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _data._label
+                            ) != -1
+                          )
+                            return true;
+                          i = items.eq(e);
+                          d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _data._label
+                            ) != 1
+                          )
+                            return true;
+                          var m = s + Math.round((e - s) / 2);
+                          i = items.eq(m);
+                          d = ctx.t.itemData(i);
+                          var res = pgAdmin.natural_sort(d._label, _data._label);
+                          if (res == 0)
+                            return true;
+
+                          if (res == -1) {
+                            s = m + 1;
+                            e--;
+                          } else {
+                            s++;
+                            e = m - 1;
+                          }
+                        }
+                        return linearSearch();
+                      };
+
+                  if (binarySearch()) {
+                    ctx.t.before(i, {
+                      itemData: _data,
+                      success: function() {
+                        if (
+                          ctx.o && ctx.o.success && typeof(ctx.o.success) == 'function'
+                        ) {
+                          ctx.o.success.apply(ctx.t, [i, _data]);
+                        }
+                      },
+                      fail: function() {
+                        console.log('Failed to add before..');
+                        if (
+                          ctx.o && ctx.o.fail && typeof(ctx.o.fail) == 'function'
+                        ) {
+                          ctx.o.fail.apply(ctx.t, [i, _data]);
+                        }
+                      }
+                    });
+                  } else {
+                    var _append = function() {
+                          var ctx = this,
+                            is_parent_loaded_before = ctx.t.wasLoad(ctx.i),
+                            _parent_data = ctx.t.itemData(ctx.i);
+
+                          ctx.t.append(ctx.i, {
+                            itemData: _data,
+                            success: function(item, options) {
+                              var i = $(options.items[0]);
+                              // Open the item path only if its parent is loaded
+                              // or parent type is same as nodes
+                              if(
+                                is_parent_loaded_before &&
+                                _parent_data &&  _parent_data._type.search(
+                                  _data._type
+                                ) > -1
+                              ) {
+                                ctx.t.openPath(i);
+                                ctx.t.select(i);
+                              } else {
+                                if (_parent_data) {
+                                  // Unload the parent node so that we'll get
+                                  // latest data when we try to expand it
+                                  ctx.t.unload(ctx.i, {
+                                    success: function (item, options) {
+                                      // Lets try to load it now
+                                      ctx.t.open(item);
+                                    }
+                                  });
+                                }
+                              }
+                              if (
+                                ctx.o && ctx.o.success &&
+                                typeof(ctx.o.success) == 'function'
+                              ) {
+                                ctx.o.success.apply(ctx.t, [i, _data]);
+                              }
+                            },
+                            fail: function() {
+                              console.log('Failed to append');
+                              if (
+                                ctx.o && ctx.o.fail &&
+                                typeof(ctx.o.fail) == 'function'
+                              ) {
+                                ctx.o.fail.apply(ctx.t, [ctx.i, _data]);
+                              }
+                            }
+                          });
+                        }.bind(ctx);
+
+                    if (ctx.i && !ctx.t.isInode(ctx.i)) {
+                        ctx.t.setInode(ctx.i, {success: _append});
+                    } else {
+                        // Handle case for node without parent i.e. server-group
+                        // or if parent node's inode is true.
+                        _append();
+                    }
+                  }
+                }.bind(ctx);
+
+            // Parent node do not have any children, let me unload it.
+            if (!first && ctx.t.wasLoad(ctx.i)) {
+              ctx.t.unload(ctx.i, {
+                success: function() {
+                  findChildNode(
+                    selectNode,
+                    function() {
+                      var o = this && this.o;
+                      if (
+                        o && o.fail && typeof(o.fail) == 'function'
+                      ) {
+                        o.fail.apply(this.t, [this.i, _data]);
+                      }
+                    }.bind(this)
+                  );
+                }.bind(this),
+                fail: function() {
+                  var o = this && this.o;
+                  if (
+                    o && o.fail && typeof(o.fail) == 'function'
+                  ) {
+                    o.fail.apply(this.t, [this.i, _data]);
+                  }
+                }.bind(this)
+              });
+              return;
+            }
+
+            // We can find the collection node using _findTreeChildNode
+            // indirectly.
+            findChildNode(selectNode, addNode);
+          }.bind(ctx);
+
+      if (!ctx.t.wasInit() || !_data) {
+        return;
+      }
+      _data._label = _data.label;
+      _data.label = _.escape(_data.label);
+
+      traversePath();
+    },
+
+    onUpdateTreeNode: function(_old, _new, _hierarchy, _opts) {
+      var ctx = {
+            b: this, // Browser
+            d: null, // current parent
+            i: null, // current item
+            hasId: true,
+            p: _.toArray(_hierarchy || {}).sort(
+              function(a, b) {
+                return (a.priority === b.priority) ? 0 : (
+                  a.priority < b.priority ? -1 : 1
+                );
+              }
+            ), // path of the old object
+            pI: [], // path items
+            t: this.tree, // Tree Api
+            o: _opts,
+            load: true,
+            old: _old,
+            new: _new,
+            op: null
+          },
+          errorOut = function() {
+            var fail = this.o && this.o.fail;
+            if (fail && typeof(fail) == 'function') {
+              fail.apply(this.t, [this.i, _new, _old]);
+            }
+          }.bind(ctx),
+          deleteNode = function() {
+            var self = this,
+                pI = this.pI,
+                findParent = function() {
+                  if (pI.length) {
+                    pI.pop();
+                    var length = pI.length;
+                    this.i = (length && pI[length - 1].item) || null;
+                    this.d = (length && pI[length - 1].d) || null;
+
+                    // It is a collection item, let's find the node item
+                    if (length && pI[length - 1].coll) {
+                      pI.pop();
+                      length = pI.length;
+                      this.i = (length && pI[length - 1].item) || null;
+                      this.d = (length && pI[length - 1].d) || null;
+                    }
+                  } else {
+                    this.i = null;
+                    this.d = null;
+                  }
+                }.bind(this);
+
+            var _item_parent = (this.i
+                                    && this.t.hasParent(this.i)
+                                    && this.t.parent(this.i)) || null,
+                _item_grand_parent = _item_parent ?
+                                        (this.t.hasParent(_item_parent)
+                                          && this.t.parent(_item_parent))
+                                        : null;
+
+            // Remove the current node first.
+            if (
+              this.i && this.d && this.old._id == this.d._id &&
+              this.old._type == this.d._type
+            ) {
+              var _parent = this.t.parent(this.i) || null;
+
+              // If there is no parent then just update the node
+              if(_parent.length == 0 && ctx.op == 'UPDATE') {
+                updateNode();
+              } else {
+                var postRemove = function() {
+                  // If item has parent but no grand parent
+                  if (_item_parent && !_item_grand_parent) {
+                    var parent = null;
+                    // We need to search in all parent siblings (eg: server groups)
+                    var parents = this.t.siblings(this.i) || [];
+                    parents.push(this.i[0])
+                    _.each(parents, function (p) {
+                      var d = self.t.itemData($(p));
+                      // If new server group found then assign it parent
+                      if(d._id == self.new._pid) {
+                        parent = p;
+                        self.pI.push({coll: true, item: parent, d: d});
+                      }
+                    });
+
+                    if (parent) {
+                      this.load = true;
+
+                      this.success = function() {
+                        addItemNode();
+                      }.bind(this);
+                      // We can refresh the collection node, but - let's not bother about
+                      // it right now.
+                      this.notFound = errorOut;
+
+                      var _d = {_id: this.new._pid, _type: self.d._type}
+                        parent = $(parent);
+                        var loaded = this.t.wasLoad(parent),
+                        onLoad = function() {
+                          self.i = parent;
+                          self.d = self.d;
+                          self.pI.push({coll: false, item: parent, d: self.d});
+                          self.success();
+                          return;
+                        };
+
+                      if (!loaded && self.load) {
+                        self.t.open(parent, {
+                          success: onLoad,
+                            unanimated: true,
+                            fail: function() {
+                              var fail = self && self.o && self.o.fail;
+
+                              if (
+                                fail && typeof(fail) == 'function'
+                              ) {
+                                fail.apply(self.t, []);
+                              }
+                            }
+                          });
+                        } else {
+                          onLoad();
+                        }
+                      }
+                    return;
+                  } else {
+                    // This is for rest of the nodes
+                    var _parentData = this.d;
+                    // Find the grand-parent, or the collection node of parent.
+                    findParent();
+
+                    if (this.i) {
+                      this.load = true;
+
+                      this.success = function() {
+                        addItemNode();
+                      }.bind(this);
+                      // We can refresh the collection node, but - let's not bother about
+                      // it right now.
+                      this.notFound = errorOut;
+
+                      // Find the new parent
+                      this.b._findTreeChildNode(
+                        this.i, {_id: this.new._pid, _type: _parentData._type}, this
+                      );
+                    } else {
+                      addItemNode();
+                    }
+                    return;
+                  }
+                }.bind(this);
+
+                // If there is a parent then we can remove the node
+                this.t.remove(this.i, {
+                  success: function() {
+                    // Find the parent
+                    findParent();
+                    // If server group have no children then close it and set inode
+                    // and unload it so it can fetch new data on next expand
+                    if (_item_parent && !_item_grand_parent && _parent
+                          && self.t.children(_parent).length == 0) {
+                          self.t.setInode(_parent, {
+                            success: function() {
+                              self.t.unload(_parent, {success: function() {
+                                setTimeout(postRemove);
+                              }}
+                              );
+                            }
+                          });
+                    } else {
+                      setTimeout(postRemove);
+                    }
+                    return true;
+                  }
+                }
+              );
+            }
+
+            }
+            errorOut();
+
+          }.bind(ctx),
+          findNewParent = function(_d) {
+            var findParent = function() {
+              if (pI.length) {
+                pI.pop();
+                var length = pI.length;
+                this.i = (length && pI[length - 1].item) || null;
+                this.d = (length && pI[length - 1].d) || null;
+
+                // It is a collection item, let's find the node item
+                if (length && pI[length - 1].coll) {
+                  pI.pop();
+                  length = pI.length;
+                  this.i = (length && pI[length - 1].item) || null;
+                  this.d = (length && pI[length - 1].d) || null;
+                }
+              } else {
+                this.i = null;
+                this.d = null;
+              }
+            }.bind(this);
+
+            // old parent was not found, can we find the new parent?
+            if (this.i) {
+              this.load = true;
+              this.success = function() {
+                addItemNode();
+              }.bind(this);
+
+              if (_d._type == old._type) {
+                // We were already searching the old object under the parent.
+                findParent();
+                _d = this.d;
+                // Find the grand parent
+                findParent();
+              }
+              console.log(_d);
+              _d = this.new._pid;
+
+              // We can refresh the collection node, but - let's not bother about
+              // it right now.
+              this.notFound = errorOut;
+
+              // Find the new parent
+              this.b._findTreeChildNode(this.i, _d, this);
+            } else {
+              addItemNode();
+            }
+          }.bind(ctx),
+          updateNode = function() {
+            if (
+              this.i && this.d && this.new._type == this.d._type
+            ) {
+              var self = this,
+              _id = this.d._id;
+              if (this.new._id != this.d._id) {
+                // Found the new oid, update its node_id
+                var node_data = this.t.itemData(ctx.i);
+                node_data._id = _id = this.new._id;
+              }
+              if (this.new._id == _id) {
+                // Found the current
+                _.extend(this.d, {
+                  '_id': this.new._id,
+                  '_label': this.new._label,
+                  'label': this.new.label
+                });
+                this.t.setLabel(ctx.i, {label: this.new.label});
+                this.t.addIcon(ctx.i, {icon: this.new.icon});
+                this.t.setId(ctx.i, {id: this.new.id});
+                this.t.openPath(this.i);
+                this.t.deselect(this.i);
+
+                // select tree item after few milliseconds
+                setTimeout(function() {
+                  self.t.select(self.i);
+                }, 10);
+              }
+            }
+            var success = this.o && this.o.success;
+            if (success && typeof(success) == 'function') {
+              success.apply(this.t, [this.i, _old, _new]);
+            }
+          }.bind(ctx),
+          traversePath = function() {
+            var ctx = this, i, d;
+
+            ctx.success = traversePath;
+            if (ctx.p.length) {
+              d = ctx.p.shift();
+              // This is the node, we can now do the required operations.
+              // We should first delete the existing node, if the parent-id is
+              // different.
+              if (!ctx.p.length) {
+                if (ctx.op == 'RECREATE') {
+                  ctx.load = false;
+                  ctx.success = deleteNode;
+                  ctx.notFound = findNewParent;
+                } else {
+                  ctx.success = updateNode;
+                  ctx.notFound = errorOut;
+                }
+              }
+              ctx.b._findTreeChildNode(
+                ctx.i, d, ctx
+              );
+            } else if (ctx.p.length == 1) {
+              ctx.notFound = findNewParent;
+            }
+            return true;
+          }.bind(ctx),
+          addItemNode = function() {
+            var ctx = this,
+                first = (ctx.i || this.t.wasLoad(ctx.i)) &&
+                  this.t.first(ctx.i),
+                findChildNode = function(success, notFound) {
+                  var ctx = this;
+                  ctx.success = success;
+                  ctx.notFound = notFound;
+
+                  ctx.b._findTreeChildNode(ctx.i, _new, ctx);
+                }.bind(ctx),
+                selectNode = function() {
+                  this.t.openPath(this.i);
+                  this.t.select(this.i);
+                  if (
+                    ctx.o && ctx.o.success && typeof(ctx.o.success) == 'function'
+                  ) {
+                    ctx.o.success.apply(ctx.t, [ctx.i, _new]);
+                  }
+                }.bind(ctx),
+                addNode = function() {
+                  var ctx = this,
+                      items = ctx.t.children(ctx.i),
+                      s = 0, e = items.length - 1, i,
+                      linearSearch = function() {
+                        while (e >= s) {
+                          i = items.eq(s);
+                          var d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _new._label
+                            ) == 1
+                          )
+                            return true;
+                          s++;
+                        }
+                        if (e != items.length - 1) {
+                          i = items.eq(e);
+                          return true;
+                        }
+                        i = null;
+                        return false;
+                      },
+                      binarySearch = function() {
+                        while (e - s > 22) {
+                          i = items.eq(s);
+                          var d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _new._label
+                            ) != -1
+                          )
+                            return true;
+                          i = items.eq(e);
+                          d = ctx.t.itemData(i);
+                          if (
+                            pgAdmin.natural_sort(
+                              d._label, _new._label
+                            ) != 1
+                          )
+                            return true;
+                          var m = s + Math.round((e - s) / 2);
+                          i = items.eq(m);
+                          d = ctx.t.itemData(i);
+                          var res = pgAdmin.natural_sort(d._label, _new._label);
+                          if (res == 0)
+                            return true;
+
+                          if (res == -1) {
+                            s = m + 1;
+                            e--;
+                          } else {
+                            s++;
+                            e = m - 1;
+                          }
+                        }
+                        return linearSearch();
+                      };
+
+                  if (binarySearch()) {
+                    ctx.t.before(i, {
+                      itemData: _new,
+                      success: function() {
+                        var new_item = $(arguments[1].items[0]);
+                        ctx.t.openPath(new_item);
+                        ctx.t.select(new_item);
+                        if (
+                          ctx.o && ctx.o.success && typeof(ctx.o.success) == 'function'
+                        ) {
+                          ctx.o.success.apply(ctx.t, [i, _old, _new]);
+                        }
+                      },
+                      fail: function() {
+                        console.log('Failed to add before..');
+                        if (
+                          ctx.o && ctx.o.fail && typeof(ctx.o.fail) == 'function'
+                        ) {
+                          ctx.o.fail.apply(ctx.t, [i, _old, _new]);
+                        }
+                      }
+                    });
+                  } else {
+                    var _appendNode = function() {
+                      ctx.t.append(ctx.i, {
+                        itemData: _new,
+                        success: function() {
+                          var new_item = $(arguments[1].items[0]);
+                          ctx.t.openPath(new_item);
+                          ctx.t.select(new_item);
+                          if (
+                            ctx.o && ctx.o.success && typeof(ctx.o.success) == 'function'
+                          ) {
+                            ctx.o.success.apply(ctx.t, [ctx.i, _old, _new]);
+                          }
+                        },
+                        fail: function() {
+                          console.log('Failed to append');
+                          if (
+                            ctx.o && ctx.o.fail && typeof(ctx.o.fail) == 'function'
+                          ) {
+                            ctx.o.fail.apply(ctx.t, [ctx.i, _old, _new]);
+                          }
+                        }
+                      })
+                    };
+
+                    // If the current node's inode is false
+                    if (ctx.i && !ctx.t.isInode(ctx.i)) {
+                      ctx.t.setInode(ctx.i, {success: _appendNode});
+                    } else {
+                      // Handle case for node without parent i.e. server-group
+                      // or if parent node's inode is true.
+                      _appendNode();
+                    }
+
+                  }
+                }.bind(ctx);
+
+            // Parent node do not have any children, let me unload it.
+            if (!first && ctx.t.wasLoad(ctx.i)) {
+              ctx.t.unload(ctx.i, {
+                success: function() {
+                  findChildNode(
+                    selectNode,
+                    function() {
+                      var o = this && this.o;
+                      if (
+                        o && o.fail && typeof(o.fail) == 'function'
+                      ) {
+                        o.fail.apply(this.t, [this.i, _old, _new]);
+                      }
+                    }.bind(this)
+                  );
+                }.bind(this),
+                fail: function() {
+                  var o = this && this.o;
+                  if (
+                    o && o.fail && typeof(o.fail) == 'function'
+                  ) {
+                    o.fail.apply(this.t, [this.i, _old, _new]);
+                  }
+                }.bind(this)
+              });
+              return;
+            }
+
+            // We can find the collection node using _findTreeChildNode
+            // indirectly.
+            findChildNode(selectNode, addNode);
+          }.bind(ctx);
+
+      if (!ctx.t.wasInit() || !_new || !_old) {
+        return;
+      }
+      ctx.pI.push(_old);
+      _new._label = _new.label;
+      _new.label = _.escape(_new.label);
+
+      // We need to check if this is collection node and have
+      // children count, if yes then add count with label too
+      if(ctx.b.Nodes[_new._type].is_collection) {
+          if ('collection_count' in _old && _old['collection_count'] > 0) {
+            _new.label = _.escape(_new._label) +
+                ' <span>(' + _old['collection_count'] + ')</span>'
+          }
+      }
+
+      if (_old._pid != _new._pid || _old._label != _new._label) {
+        ctx.op = 'RECREATE';
+        traversePath();
+      } else {
+        ctx.op = 'UPDATE';
+        traversePath();
+      }
+    },
+
+    onRefreshTreeNode: function(_i, _opts) {
+      var d = _i && this.tree.itemData(_i),
+          n = d && d._type && this.Nodes[d._type],
+          ctx = {
+            b: this, // Browser
+            d: d, // current parent
+            i: _i, // current item
+            p: null, // path of the old object
+            pI: [], // path items
+            t: this.tree, // Tree Api
+            o: _opts
+          },
+          isOpen,
+          idx = -1;
+
+      if (!n) {
+        _i = null;
+        ctx.i = null;
+        ctx.d = null;
+      } else {
+        isOpen = (this.tree.isInode(_i) && this.tree.isOpen(_i));
+      }
+
+      ctx.branch = ctx.t.serialize(
+        _i, {}, function(i, el, d) {
+          idx++;
+          if (!idx || (d.inode && d.open)) {
+            return {
+              _id: d._id, _type: d._type, branch: d.branch, open: d.open
+            };
+          }
+        });
+
+      if (!n) {
+        ctx.t.destroy({
+          success: function() {
+            initializeBrowserTree(ctx.b);
+            ctx.t = ctx.b.tree;
+            ctx.i = null;
+            ctx.b._refreshNode(ctx, ctx.branch);
+          },
+          error: function() {
+            var fail = (_opts.o && _opts.o.fail) || _opts.fail;
+
+            if (typeof(fail) == 'function') {
+              fail();
+            }
+          }
+        });
+        return;
+      }
+      var fetchNodeInfo = function(_i, _d, _n) {
+            var info = _n.getTreeNodeHierarchy(_i),
+                url = _n.generate_url(_i, 'nodes', _d, true);
+
+            $.ajax({
+              url: url,
+              type: 'GET',
+              cache: false,
+              dataType: 'json',
+              success: function(res) {
+                // Node information can come as result/data
+                var data = res.result || res.data;
+
+                data._label = data.label;
+                data.label = _.escape(data.label);
+                var d = ctx.t.itemData(ctx.i);
+                _.extend(d, data);
+                ctx.t.setLabel(ctx.i, {label: _d.label});
+                ctx.t.addIcon(ctx.i, {icon: _d.icon});
+                ctx.t.setId(ctx.i, {id: _d.id});
+                ctx.t.setInode(ctx.i, {inode: data.inode});
+
+                if (
+                  _n.can_expand && typeof(_n.can_expand) == 'function'
+                ) {
+                  if (!_n.can_expand(d)) {
+                    ctx.t.unload(ctx.i);
+                    return;
+                  }
+                }
+                ctx.b._refreshNode(ctx, ctx.branch);
+                var success = (ctx.o && ctx.o.success) || ctx.success;
+                if (success && typeof(success) == 'function') {
+                  success();
+                }
+              },
+              error: function(xhr, error, status) {
+                if (
+                  !Alertify.pgHandleItemError(
+                    xhr, error, status, {item: _i, info: info}
+                  )
+                ) {
+                  var msg = xhr.responseText,
+                      contentType = xhr.getResponseHeader('Content-Type'),
+                      msg = xhr.responseText,
+                      jsonResp = (
+                        contentType &&
+                        contentType.indexOf('application/json') == 0 &&
+                        $.parseJSON(xhr.responseText)
+                        ) || {};
+
+                  if (xhr.status == 410 && jsonResp.success == 0) {
+                    var p = ctx.t.parent(ctx.i);
+
+                    ctx.t.remove(ctx.i, {
+                      success: function() {
+                        if (p) {
+                          // Try to refresh the parent on error
+                          try {
+                            pgBrowser.Events.trigger(
+                              'pgadmin:browser:tree:refresh', p
+                            );
+                          } catch(e) {}
+                        }
+                      }
+                    });
+                  }
+
+                  Alertify.pgNotifier(
+                    error, xhr, gettext("Error retrieving details for the node."),
+                    function() {
+                       console.log(arguments);
+                    }
+                  );
+                }
+              }
+            });
+          }.bind(this);
+
+      if (n && n.collection_node) {
+        var p = ctx.i = this.tree.parent(_i),
+            unloadNode = function() {
+              this.tree.unload(_i, {
+                success: function() {
+                  _i = p;
+                  d = ctx.d = ctx.t.itemData(ctx.i);
+                  n = ctx.b.Nodes[d._type];
+                  _i = p;
+                  fetchNodeInfo(_i, d, n);
+                },
+                fail: function() {
+                  console.log(arguments);
+                }
+              });
+            }.bind(this);
+        if (!this.tree.isInode(_i)) {
+          this.tree.setInode(_i, {
+            success: unloadNode
+          });
+        } else {
+          unloadNode();
+        }
+      } else if (isOpen) {
+        this.tree.unload(_i, {
+          success: fetchNodeInfo.bind(this, _i, d, n),
+          fail: function() {
+            console.log(arguments);
+          }
+        });
+      } else if (!this.tree.isInode(_i) && d.inode) {
+        this.tree.setInode(_i, {
+          success: fetchNodeInfo.bind(this, _i, d, n),
+          fail: function() {
+            console.log(arguments);
+          }
+        });
+      } else {
+        fetchNodeInfo(_i, d, n);
+      }
+    },
+
+    removeChildTreeNodesById: function(_parentNode, _collType, _childIds) {
+      var tree = pgBrowser.tree;
+      if(_parentNode && _collType) {
+        var children = tree.children(_parentNode),
+            idx = 0, size = children.length,
+            childNode, childNodeData;
+
+        _parentNode = null;
+
+        for (; idx < size; idx++) {
+          childNode = children.eq(idx);
+          childNodeData = tree.itemData(childNode);
+
+          if (childNodeData._type == _collType) {
+            _parentNode = childNode;
+            break;
+          }
+        }
+      }
+
+      if (_parentNode) {
+        var children = tree.children(_parentNode),
+            idx = 0, size = children.length,
+            childNode, childNodeData,
+            prevChildNode;
+
+        for (; idx < size; idx++) {
+          childNode = children.eq(idx);
+          childNodeData = tree.itemData(childNode);
+
+          if (_childIds.indexOf(childNodeData._id) != -1) {
+            pgBrowser.removeTreeNode(childNode, false, _parentNode);
+          }
+        }
+        return true;
+      }
+      return false;
+    },
+
+    removeTreeNode: function(_node, _selectNext, _parentNode) {
+      var tree = pgBrowser.tree,
+          nodeToSelect = null;
+
+      if (!_node)
+        return false;
+
+      if (_selectNext) {
+        nodeToSelect = tree.next(_node);
+        if (!nodeToSelect || !nodeToSelect.length) {
+          nodeToSelect = tree.prev(_node);
+
+          if (!nodeToSelect || !nodeToSelect.length) {
+            if (!_parentNode) {
+              nodeToSelect = tree.parent(_node);
+            } else {
+              nodeToSelect = _parentNode;
+            }
+          }
+        }
+        if (nodeToSelect)
+          tree.select(nodeToSelect);
+      }
+      tree.remove(_node);
+      return true;
+    },
+
+    findSiblingTreeNode: function(_node, _id) {
+      var tree = pgBrowser.tree,
+          parentNode = tree.parent(_node),
+          siblings = tree.children(parentNode),
+          idx = 0, nodeData, node;
+
+      for(; idx < siblings.length; idx++) {
+        node = siblings.eq(idx);
+        nodeData = tree.itemData(node);
+
+        if (nodeData && nodeData._id == _id)
+          return node;
+      }
+      return null;
+    },
+
+    findParentTreeNodeByType: function(_node, _parentType) {
+      var tree = pgBrowser.tree,
+          nodeData,
+          node = _node;
+
+      do {
+        nodeData = tree.itemData(node);
+        if (nodeData && nodeData._type == _parentType)
+          return node;
+        node = tree.hasParent(node) ? tree.parent(node) : null;
+      } while (node);
+
+      return null;
+    },
+
+    findChildCollectionTreeNode: function(_node, _collType) {
+      var tree = pgBrowser.tree,
+          nodeData, idx = 0,
+          node = _node,
+          children = _node && tree.children(_node);
+
+      if (!children || !children.length)
+        return null;
+
+      for(; idx < children.length; idx++) {
+        node = children.eq(idx);
+        nodeData = tree.itemData(node);
+
+        if (nodeData && nodeData._type == _collType)
+          return node;
+      }
+      return null;
+    },
+
+    addChildTreeNodes: function(_treeHierarchy, _node, _type, _arrayIds, _callback) {
+      var module = _type in pgBrowser.Nodes && pgBrowser.Nodes[_type],
+          childTreeInfo = _arrayIds.length && _.extend(
+            {}, _.mapObject(
+              _treeHierarchy, function(_val, _key) {
+                _val.priority -= 1; return _val;
+              })
+          ),
+          arrayChildNodeData = [],
+          fetchNodeInfo = function(_callback) {
+            if (!_arrayIds.length) {
+              if (_callback) {
+                _callback();
+              }
+              return;
+            }
+
+            var childDummyInfo = {
+                  '_id': _arrayIds.pop(), '_type': _type, 'priority': 0
+                },
+                childNodeUrl;
+            childTreeInfo[_type] = childDummyInfo;
+
+            childNodeUrl = module.generate_url(
+              null, 'nodes', childDummyInfo, true, childTreeInfo
+            );
+            console.debug("Fetching node information using: ", childNodeUrl);
+
+            $.ajax({
+              url: childNodeUrl,
+              dataType: "json",
+              success: function(res) {
+                if (res.success) {
+                  arrayChildNodeData.push(res.data);
+                }
+                fetchNodeInfo(_callback);
+              },
+              error: function(xhr, status, error) {
+                try {
+                  var err = $.parseJSON(xhr.responseText);
+                  if (err.success == 0) {
+                    var alertifyWrapper = new AlertifyWrapper();
+                    alertifyWrapper.error(err.errormsg);
+                  }
+                } catch (e) {}
+                fetchNodeInfo(_callback);
+              }
+            });
+          };
+
+
+      if (!module) {
+        console.warning(
+          "Developer: Couldn't find the module for the given child: ",
+          _.clone(arguments)
+        );
+        return;
+      }
+
+      if (pgBrowser.tree.wasLoad(_node) || pgBrowser.tree.isLeaf(_node)) {
+        fetchNodeInfo(function() {
+          console.log('Append this nodes:', arrayChildNodeData);
+          _.each(arrayChildNodeData, function(_nodData) {
+            pgBrowser.Events.trigger(
+              'pgadmin:browser:tree:add', _nodData, _treeHierarchy
+            );
+          });
+
+          if (_callback) {
+            _callback();
+          }
+        });
+      } else {
+        if (_callback) {
+          _callback();
+        }
+      }
+    },
+
+    _refreshNode: function(_ctx, _d) {
+      var traverseNodes = function(_d) {
+            var _ctx = this, idx = 0, ctx, d,
+                size = (_d.branch && _d.branch.length) || 0,
+                findNode = function(_i, __d, __ctx) {
+                  setTimeout(
+                    function() {
+                      __ctx.b._findTreeChildNode(_i, __d, __ctx);
+                    }, 0
+                  );
+                };
+
+            for (; idx < size; idx++) {
+              d = _d.branch[idx];
+              var n = _ctx.b.Nodes[d._type];
+              ctx = {
+                b: _ctx.b,
+                t: _ctx.t,
+                pI: [],
+                i: _ctx.i,
+                d: d,
+                select: _ctx.select,
+                hasId: n && !n.collection_node,
+                o: _ctx.o,
+                load: true
+              };
+              ctx.success = function() {
+                this.b._refreshNode.call(this.b, this, this.d);
+              }.bind(ctx)
+              findNode(_ctx.i, d, ctx);
+            }
+          }.bind(_ctx, _d);
+
+       if (!_d || !_d.open)
+         return;
+
+       if (!_ctx.t.isOpen(_ctx.i)) {
+         _ctx.t.open(_ctx.i, {
+           unanimated: true,
+           success: traverseNodes,
+           fail: function() { /* Do nothing */ }
+         });
+       } else {
+         traverseNodes();
+       }
+
+    },
+
+    editor_shortcut_keys: {
+      // Autocomplete sql command
+      "Ctrl-Space": "autocomplete",
+      "Cmd-Space": "autocomplete",
+
+      // Select All text
+      "Ctrl-A": "selectAll",
+      "Cmd-A": "selectAll",
+
+      // Redo text
+      "Ctrl-Y": "redo",
+      "Cmd-Y": "redo",
+
+      // Undo text
+      "Ctrl-Z": "undo",
+      "Cmd-Z": "undo",
+
+      // Delete Line
+      "Ctrl-D": "deleteLine",
+      "Cmd-D": "deleteLine",
+
+      // Go to start/end of Line
+      "Alt-Left": "goLineStart",
+      "Alt-Right": "goLineEnd",
+
+      // Move word by word left/right
+      "Ctrl-Alt-Left": "goGroupLeft",
+      "Cmd-Alt-Left": "goGroupLeft",
+      "Ctrl-Alt-Right": "goGroupRight",
+      "Cmd-Alt-Right": "goGroupRight",
+
+      // Allow user to delete Tab(s)
+      "Shift-Tab": "indentLess"
+    },
+    editor_options: {
+      tabSize: pgBrowser.utils.tabSize,
+      wrapCode: pgBrowser.utils.wrapCode,
+      insert_pair_brackets: pgBrowser.utils.insertPairBrackets,
+      brace_matching: pgBrowser.utils.braceMatching
+    }
+
+  });
+
+  /* Remove paste event mapping from CodeMirror's emacsy KeyMap binding
+   * specific to Mac LineNumber:5797 - lib/Codemirror.js
+   * It is preventing default paste event(Cmd-V) from triggering
+   * in runtime.
+   */
+  delete CodeMirror.keyMap.emacsy["Ctrl-V"];
+
+  // Use spaces instead of tab
+  if (pgBrowser.utils.useSpaces == 'True') {
+    pgAdmin.Browser.editor_shortcut_keys.Tab = "insertSoftTab";
+  }
+
+  window.onbeforeunload = function(ev) {
+    var e = ev || window.event,
+        msg = S(gettext('Are you sure you wish to close the %s browser?')).sprintf(pgBrowser.utils.app_name).value();
+
+    // For IE and Firefox prior to version 4
+    if (e) {
+      e.returnValue = msg;
+    }
+
+    // For Safari
+    return msg;
+  };
+
+  return pgAdmin.Browser;
+});
diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js
new file mode 100644
index 00000000..72570c75
--- /dev/null
+++ b/web/pgadmin/browser/static/js/collection.js
@@ -0,0 +1,176 @@
+define([
+  'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+  'backbone', 'alertify', 'backform', 'backgrid', 'pgadmin.backform', 'pgadmin.backgrid',
+  'pgadmin.browser.node'
+], function(gettext, $, _, S, pgAdmin, Backbone, Alertify, Backform, Backgrid) {
+
+  var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
+
+  // It has already been defined.
+  // Avoid running this script again.
+  if (pgBrowser.Collection)
+    return pgBrowser.Collection;
+
+  pgBrowser.Collection = function() {};
+
+   _.extend(
+     pgBrowser.Collection,
+     _.clone(pgBrowser.Node), {
+    ///////
+    // Initialization function
+    // Generally - used to register the menus for this type of node.
+    //
+    // Also, look at pgAdmin.Browser.add_menus(...) function.
+    //
+    // Collection will not have 'Properties' menu.
+    //
+    // NOTE: Override this for each node for initialization purpose
+    Init: function() {
+      if (this.node_initialized)
+        return;
+      this.node_initialized = true;
+      pgAdmin.Browser.add_menus([{
+        name: 'refresh', node: this.type, module: this,
+        applies: ['object', 'context'], callback: 'refresh',
+        priority: 1, label: gettext('Refresh...'),
+        icon: 'fa fa-refresh'
+      }]);
+
+      // show query tool only in context menu of supported nodes.
+      if (pgAdmin.DataGrid && pgAdmin.unsupported_nodes) {
+        if (_.indexOf(pgAdmin.unsupported_nodes, this.type) == -1) {
+          pgAdmin.Browser.add_menus([{
+            name: 'show_query_tool', node: this.type, module: this,
+            applies: ['context'], callback: 'show_query_tool',
+            priority: 998, label: gettext('Query Tool...'),
+            icon: 'fa fa-bolt'
+          }]);
+        }
+      }
+    },
+    hasId: false,
+    is_collection: true,
+    collection_node: true,
+    // A collection will always have a collection of statistics, when the node
+    // it represent will have some statistics.
+    hasCollectiveStatistics: true,
+    showProperties: function(item, data, panel) {
+      var that = this,
+        j = panel.$container.find('.obj_properties').first(),
+        view = j.data('obj-view'),
+        content = $('<div></div>')
+          .addClass('pg-prop-content col-xs-12'),
+        node = pgBrowser.Nodes[that.node],
+        // This will be the URL, used for object manipulation.
+        urlBase = this.generate_url(item, 'properties', data),
+        collection = new (node.Collection.extend({
+          url: urlBase,
+          model: node.model
+        }))(),
+        info = this.getTreeNodeHierarchy.apply(this, [item]),
+        gridSchema = Backform.generateGridColumnsFromModel(
+            info, node.model, 'properties', that.columns
+          ),
+        // Initialize a new Grid instance
+        grid = new Backgrid.Grid({
+          columns: gridSchema.columns,
+          collection: collection,
+          className: "backgrid table-bordered"
+        }),
+        gridView = {
+          'remove': function() {
+            if (this.grid) {
+              if (this.grid.collection) {
+                this.grid.collection.reset(null, {silent: true});
+                delete (this.grid.collection);
+              }
+              delete (this.grid);
+              this.grid = null;
+            }
+          },
+          grid: grid
+        };
+
+      if (view) {
+        // Avoid unnecessary reloads
+        if (_.isEqual($(panel).data('node-prop'), urlBase)) {
+          return;
+        }
+
+        // Cache the current IDs for next time
+        $(panel).data('node-prop', urlBase);
+
+        // Reset the data object
+        j.data('obj-view', null);
+      }
+
+      // Make sure the HTML element is empty.
+      j.empty();
+      j.data('obj-view', gridView);
+
+      // Render subNode grid
+      content.append(grid.render().$el);
+      j.append(content);
+
+      // Fetch Data
+      collection.fetch({
+        reset: true,
+        error: function(xhr, error, message) {
+          pgBrowser.Events.trigger(
+            'pgadmin:collection:retrieval:error', 'properties', xhr, error, message, item, that
+          );
+          if (
+            !Alertify.pgHandleItemError(xhr, error, message, {item: item, info: info})
+          ) {
+            Alertify.pgNotifier(
+              error, xhr,
+              S(
+                gettext("Error retrieving properties - %s.")
+              ).sprintf(message || that.label).value(),
+              function() {
+                console.log(arguments);
+              }
+            );
+          }
+        }
+      })
+    },
+    generate_url: function(item, type, d) {
+      var url = pgAdmin.Browser.URL + '{TYPE}/{REDIRECT}{REF}',
+        /*
+         * Using list, and collection functions of a node to get the nodes
+         * under the collection, and properties of the collection respectively.
+         */
+        opURL = {
+          'properties': 'obj', 'children': 'nodes'
+        },
+        ref = '', self = this;
+
+      _.each(
+        _.sortBy(
+          _.values(
+           _.pick(
+            this.getTreeNodeHierarchy(item), function(v, k, o) {
+              return (k != self.type);
+            })
+           ),
+          function(o) { return o.priority; }
+          ),
+        function(o) {
+          ref = S('%s/%s').sprintf(ref, encodeURI(o._id)).value();
+        });
+
+      var args = {
+        'TYPE': self.node,
+        'REDIRECT': (type in opURL ? opURL[type] : type),
+        'REF': S('%s/').sprintf(ref).value()
+      };
+
+      return url.replace(/{(\w+)}/g, function(match, arg) {
+        return args[arg];
+      });
+    }
+  });
+
+  return pgBrowser.Collection;
+});
diff --git a/web/pgadmin/browser/static/js/error.js b/web/pgadmin/browser/static/js/error.js
new file mode 100644
index 00000000..7f845af6
--- /dev/null
+++ b/web/pgadmin/browser/static/js/error.js
@@ -0,0 +1,45 @@
+define(
+    ['sources/gettext', 'underscore', 'alertify', 'pgadmin'],
+function(gettext, _, alertify, pgAdmin) {
+  pgAdmin.Browser = pgAdmin.Browser || {};
+
+  _.extend(pgAdmin.Browser, {
+    report_error: function(title, message, info) {
+      var text = '<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">\
+           <div class="panel panel-default">\
+           <div class="panel-heading" role="tab" id="headingOne">\
+           <h4 class="panel-title">\
+           <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">\
+           ' + gettext("Error message") + '</a>\
+        </h4>\
+        </div>\
+        <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">\
+        <div class="panel-body" style="overflow: auto;">' + unescape(message) + '</div>\
+        </div>\
+        </div>';
+
+      if (info != null && info != '') {
+        text += '<div class="panel panel-default">\
+            <div class="panel-heading" role="tab" id="headingTwo">\
+            <h4 class="panel-title">\
+            <a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">\
+            ' + gettext("Additional info") + '</a>\
+          </h4>\
+          </div>\
+          <div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">\
+          <div class="panel-body" style="overflow: auto;">' + unescape(info) + '</div>\
+          </div>\
+          </div>\
+          </div>'
+      }
+
+      text += '</div>';
+      alertify.alert(
+        title,
+        text
+      )
+    },
+  });
+
+  return pgAdmin.Browser.report_error;
+});
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
new file mode 100644
index 00000000..9a36dc5d
--- /dev/null
+++ b/web/pgadmin/browser/static/js/node.js
@@ -0,0 +1,1578 @@
+define(
+  'pgadmin.browser.node', [
+    'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'pgadmin',
+    'pgadmin.browser.menu', 'backbone', 'alertify', 'pgadmin.browser.datamodel',
+    'backform', 'pgadmin.browser.utils', 'pgadmin.backform', 'pgadmin.alertifyjs'
+], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
+
+  var wcDocker = window.wcDocker,
+    keyCode = {
+      ENTER: 13,
+      ESCAPE: 27,
+      F1: 112
+    };
+
+  // It has already been defined.
+  // Avoid running this script again.
+  if (pgBrowser.Node)
+    return pgBrowser.Node;
+
+  pgBrowser.Nodes = pgBrowser.Nodes || {};
+
+  // A helper (base) class for all the nodes, this has basic
+  // operations/callbacks defined for basic operation.
+  pgBrowser.Node = function() {};
+
+  // Helper function to correctly set up the property chain, for subclasses.
+  // 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) {
+    var parent = this;
+    var child;
+
+    // The constructor function for the new subclass is defined to simply call
+    // the parent's constructor.
+    child = function(){ return parent.apply(this, arguments); };
+
+    // Add static properties to the constructor function, if supplied.
+    _.extend(child, parent, _.omit(props, 'callbacks'));
+
+    // Make sure - a child have all the callbacks of the parent.
+    child.callbacks = _.extend({}, parent.callbacks, props.callbacks);
+
+    var bindToChild = function(cb) {
+          if (typeof(child.callbacks[cb]) == 'function') {
+            child.callbacks[cb] = child.callbacks[cb].bind(child);
+          }
+        },
+        callbacks = _.keys(child.callbacks);
+    for(var idx = 0; idx < callbacks.length; idx++) bindToChild(callbacks[idx]);
+
+    // Registering the node by calling child.Init(...) function
+    child.Init.apply(child);
+
+    // Initialize the parent
+    this.Init.apply(child);
+
+    return child;
+  };
+
+  _.extend(pgAdmin.Browser.Node, Backbone.Events, {
+    // Node type
+    type: undefined,
+    // Label
+    label: '',
+    // Help pages
+    sqlAlterHelp: '',
+    sqlCreateHelp: '',
+    dialogHelp: '',
+
+    title: function(o, d) {
+      return o.label + (d ? (' - ' + d.label) : '');
+    },
+    hasId: true,
+    ///////
+    // Initialization function
+    // Generally - used to register the menus for this type of node.
+    //
+    // Also, look at pgAdmin.Browser.add_menus(...) function.
+    //
+    // NOTE: Override this for each node for initialization purpose
+    Init: function() {
+      var self = this;
+      if (self.node_initialized)
+        return;
+      self.node_initialized = true;
+
+      pgAdmin.Browser.add_menus([{
+        name: 'refresh', node: self.type, module: self,
+        applies: ['object', 'context'], callback: 'refresh',
+        priority: 1, label: gettext('Refresh...'),
+        icon: 'fa fa-refresh'
+      }]);
+
+      if (self.canEdit) {
+        pgAdmin.Browser.add_menus([{
+        name: 'show_obj_properties', node: self.type, module: self,
+        applies: ['object', 'context'], callback: 'show_obj_properties',
+        priority: 999, label: gettext('Properties...'),
+        data: {'action': 'edit'}, icon: 'fa fa-pencil-square-o'
+      }]);
+      }
+
+      if (self.canDrop) {
+        pgAdmin.Browser.add_menus([{
+          name: 'delete_object', node: self.type, module: self,
+          applies: ['object', 'context'], callback: 'delete_obj',
+          priority: 2, label: gettext('Delete/Drop'),
+          data: {'url': 'drop'}, icon: 'fa fa-trash',
+          enable: _.isFunction(self.canDrop) ?
+            function() {
+              return !!(self.canDrop.apply(self, arguments));
+            } : (!!self.canDrop)
+        }]);
+        if (self.canDropCascade) {
+          pgAdmin.Browser.add_menus([{
+            name: 'delete_object_cascade', node: self.type, module: self,
+            applies: ['object', 'context'], callback: 'delete_obj',
+            priority: 3, label: gettext('Drop Cascade'),
+            data: {'url': 'delete'}, icon: 'fa fa-trash',
+            enable: _.isFunction(self.canDropCascade) ?
+              function() { return self.canDropCascade.apply(self, arguments); } : (!!self.canDropCascade)
+          }]);
+        }
+      }
+
+      // show query tool only in context menu of supported nodes.
+      if (true) {
+        if (_.indexOf(pgAdmin.unsupported_nodes, self.type) == -1) {
+          pgAdmin.Browser.add_menus([{
+            name: 'show_query_tool', node: self.type, module: self,
+            applies: ['context'], callback: 'show_query_tool',
+            priority: 998, label: gettext('Query Tool...'),
+            icon: 'fa fa-bolt',
+            enable: function(itemData, item, data) {
+              if (itemData._type == 'database' && itemData.allowConn)
+                return true;
+              else if(itemData._type != 'database')
+                return true;
+              else
+                return false;
+            }
+          }]);
+        }
+      }
+
+      // This will add options of scripts eg:'CREATE Script'
+      if (self.hasScriptTypes && _.isArray(self.hasScriptTypes)
+        &&  self.hasScriptTypes.length > 0) {
+          // For each script type create menu
+          _.each(self.hasScriptTypes, function(stype) {
+
+            var type_label = S(
+                gettext("%s Script")
+                ).sprintf(stype.toUpperCase()).value(),
+              stype = stype.toLowerCase();
+
+            // Adding menu for each script type
+            pgAdmin.Browser.add_menus([{
+              name: 'show_script_' + stype, node: self.type, module: self,
+              applies: ['object', 'context'], callback: 'show_script',
+              priority: 4, label: type_label, category: 'Scripts',
+              data: {'script': stype}, icon: 'fa fa-pencil',
+              enable: self.check_user_permission
+            }]);
+          });
+      }
+    },
+    ///////
+    // Checks if Script Type is allowed to user
+    // First check if role node & create role allowed
+    // Otherwise test rest of database objects
+    // if no permission matched then do not allow create script
+    ///////
+    check_user_permission: function(itemData, item, data) {
+      // Do not display CREATE script on server group and server node
+      if (itemData._type == 'server_group' || itemData._type == 'server') {
+        return false;
+      }
+
+      // Do not display the menu if the database connection is not allowed
+      if (itemData._type == 'database' && !itemData.allowConn)
+        return false;
+
+      var node = pgBrowser.Nodes[itemData._type],
+        parentData = node.getTreeNodeHierarchy(item);
+      if ( _.indexOf(['create','insert','update', 'delete'], data.script) != -1) {
+        if (itemData.type == 'role' &&
+          parentData.server.user.can_create_role) {
+          return true;
+        } else if (
+           (
+            parentData.server && (
+            parentData.server.user.is_superuser ||
+            parentData.server.user.can_create_db)
+           ) ||
+           (
+            parentData.schema && parentData.schema.can_create
+           )
+           ) {
+            return true;
+        } else {
+           return false;
+        }
+      } else {
+        return true;
+      }
+    },
+    ///////
+    // Generate a Backform view using the node's model type
+    //
+    // Used to generate view for the particular node properties, edit,
+    // creation.
+    getView: function(item, type, el, node, formType, callback, ctx, cancelFunc) {
+      var that = this;
+
+      if (!this.type || this.type == '')
+        // We have no information, how to generate view for this type.
+        return null;
+
+      if (this.model) {
+        // This will be the URL, used for object manipulation.
+        // i.e. Create, Update in these cases
+        var urlBase = this.generate_url(item, type, node, false);
+
+        if (!urlBase)
+          // Ashamed of myself, I don't know how to manipulate this
+          // node.
+          return null;
+
+        var attrs = {};
+
+        // In order to get the object data from the server, we must set
+        // object-id in the model (except in the create mode).
+        if (type !== 'create') {
+          attrs[this.model.idAttribute || this.model.prototype.idAttribute ||
+            'id'] = node._id;
+        }
+
+        // We know - which data model to be used for this object.
+        var info = this.getTreeNodeHierarchy.apply(this, [item]),
+            newModel = new (this.model.extend({urlRoot: urlBase})) (
+                attrs, {node_info: info}
+                ),
+            fields = Backform.generateViewSchema(
+                info, newModel, type, this, node
+                );
+
+        if (type == 'create' || type == 'edit') {
+
+          if (callback && ctx) {
+              callback = callback.bind(ctx);
+          } else {
+            callback = function() {
+              console.log("Broke something!!! Why we don't have the callback or the context???");
+            };
+          }
+
+          var onSessionInvalid = function(msg) {
+            var alertMessage = '\
+              <div class="media error-in-footer bg-red-1 border-red-2 font-red-3 text-14">\
+                <div class="media-body media-middle">\
+                  <div class="alert-icon error-icon">\
+                    <i class="fa fa-exclamation-triangle" aria-hidden="true"></i>\
+                  </div>\
+                    <div class="alert-text">' + msg + '</div>\
+                </div>\
+              </div>';
+            if(!_.isUndefined(that.statusBar)) {
+              that.statusBar.html(alertMessage).css("visibility", "visible");
+            }
+            callback(true);
+
+            return true;
+          };
+
+          var onSessionValidated =  function(sessHasChanged) {
+
+            if(!_.isUndefined(that.statusBar)) {
+              that.statusBar.empty().css("visibility", "hidden");
+            }
+
+            callback(false, sessHasChanged);
+          };
+
+          callback(false, false);
+
+          newModel.on('pgadmin-session:valid', onSessionValidated);
+          newModel.on('pgadmin-session:invalid', onSessionInvalid);
+        }
+        // 'schema' has the information about how to generate the form.
+        if (_.size(fields)) {
+          // This will contain the actual view
+          var view;
+
+          if (formType == 'fieldset') {
+            // It is used to show, edit, create the object in the
+            // properties tab.
+            view = new Backform.Fieldset({
+              el: el, model: newModel, schema: fields
+            });
+          } else {
+            // This generates a view to be used by the node dialog
+            // (for create/edit operation).
+            view = new Backform.Dialog({
+              el: el, model: newModel, schema: fields
+            });
+          }
+
+          var setFocusOnEl = function() {
+            setTimeout(function() {
+              $(el).find('.tab-pane.active:first').find('input:first').focus();
+            }, 500);
+          };
+
+          if (!newModel.isNew()) {
+            // This is definetely not in create mode
+            var msgDiv = '<div class="alert alert-info pg-panel-message pg-panel-properties-message">'+
+                gettext("Retrieving data from the server...") + '</div>',
+                $msgDiv = $(msgDiv);
+            var timer = setTimeout(function(ctx) {
+              // notify user if request is taking longer than 1 second
+
+              if (!_.isUndefined(ctx)) {
+                $msgDiv.appendTo(ctx);
+              }
+            }, 1000, ctx);
+
+            newModel.fetch({
+              success: function(res, msg, xhr) {
+                // clear timeout and remove message
+                clearTimeout(timer);
+                $msgDiv.addClass('hidden');
+
+                // We got the latest attributes of the
+                // object. Render the view now.
+                view.render();
+                setFocusOnEl();
+                newModel.startNewSession();
+              },
+              error: function(xhr, error, message) {
+                var _label = that && item ?
+                              that.getTreeNodeHierarchy(
+                                item
+                              )[that.type].label : '';
+                pgBrowser.Events.trigger(
+                  'pgadmin:node:retrieval:error', 'properties',
+                  xhr, error, message, item
+                );
+                if (
+                  !Alertify.pgHandleItemError(
+                    xhr, error, message, {item: item, info: info}
+                  )
+                ) {
+                  Alertify.pgNotifier(
+                    error, xhr,
+                    S(
+                      gettext("Error retrieving properties - %s")
+                    ).sprintf(message || _label).value(),
+                    function() {
+                      console.log(arguments);
+                    }
+                  );
+                }
+                // Close the panel (if could not fetch properties)
+                if (cancelFunc) {
+                  cancelFunc();
+                }
+              }
+            });
+          } else {
+            // Yay - render the view now!
+            // $(el).focus();
+            view.render();
+            setFocusOnEl();
+            newModel.startNewSession();
+          }
+        }
+        return view;
+      }
+
+      return null;
+    },
+    register_node_panel: function() {
+      var w = pgBrowser.docker,
+        p = w.findPanels('node_props');
+
+      if (p && p.length == 1)
+        return;
+
+      var events = {};
+      events[wcDocker.EVENT.RESIZE_ENDED] = function() {
+        var $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
+          });
+
+        }
+      };
+
+      p = new pgBrowser.Panel({
+          name: 'node_props',
+          showTitle: true,
+          isCloseable: true,
+          isPrivate: true,
+          elContainer: true,
+          content: '<div class="obj_properties"><div class="alert alert-info pg-panel-message">' + gettext('Please wait while we fetch information about the node from the server!') + '</div></div>',
+          onCreate: function(myPanel, $container) {
+            $container.addClass('pg-no-overflow');
+          },
+          events: events
+      });
+      p.load(pgBrowser.docker);
+    },
+    /*
+     * Default script type menu for node.
+     *
+     * Override this, to show more script type menus (e.g hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'])
+     *
+     * Or set it to empty array to disable script type menu on node (e.g hasScriptTypes: [])
+     */
+    hasScriptTypes: ['create'],
+    /******************************************************************
+     * This function determines the given item is editable or not.
+     *
+     * Override this, when a node is not editable.
+     */
+    canEdit: true,
+    /******************************************************************
+     * This function determines the given item is deletable or not.
+     *
+     * Override this, when a node is not deletable.
+     */
+    canDrop: false,
+    /************************************************************************
+     * This function determines the given item and children are deletable or
+     * not.
+     *
+     * Override this, when a node is not deletable.
+     */
+    canDropCascade: false,
+    // List of common callbacks - that can be used for different
+    // operations!
+    callbacks: {
+      /******************************************************************
+       * This function allows to create/edit/show properties of any
+       * object depending on the arguments provided.
+       *
+       * args must be a object containing:
+       *   action - create/edit/properties
+       *   item   - The properties of the item (tree ndoe item)
+       *
+       * NOTE:
+       * if item is not provided, the action will be done on the
+       * currently selected tree item node.
+       *
+       **/
+      show_obj_properties: function(args, item) {
+        var t = pgBrowser.tree,
+          i = args.item || item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined,
+          o = this,
+          l = o.title.apply(this, [d]);
+
+        // Make sure - the properties dialog type registered
+        pgBrowser.Node.register_node_panel();
+
+        // No node selected.
+        if (!d)
+          return;
+
+        var self = this,
+            isParent = (_.isArray(this.parent_type) ?
+              function(d) {
+                return (_.indexOf(self.parent_type, d._type) != -1);
+              } : function(d) {
+                return (self.parent_type == d._type);
+              }),
+            addPanel = function() {
+              var d = window.document,
+                  b = d.body,
+                  el = d.createElement('div');
+
+              d.body.insertBefore(el, d.body.firstChild);
+
+              var pW = screen.width < 800 ? '95%' : '500px',
+                  pH = screen.height < 600 ? '95%' : '550px',
+                  w = pgAdmin.toPx(el, self.width || pW, 'width', true),
+                  h = pgAdmin.toPx(el, self.height|| pH, 'height', true),
+                  x = (b.offsetWidth - w) / 2,
+                  y = (b.offsetHeight - h) / 2;
+
+              var p = pgBrowser.docker.addPanel(
+                'node_props', wcDocker.DOCK.FLOAT, undefined,
+                {w: w + 'px', h: h + 'px', x: x + 'px', y: y + 'px'}
+              );
+
+              b.removeChild(el);
+              // delete(el);
+
+              return p;
+            };
+
+        if (args.action == 'create') {
+          // If we've parent, we will get the information of it for
+          // proper object manipulation.
+          //
+          // You know - we're working with RDBMS, relation is everything
+          // for us.
+          if (self.parent_type && !isParent(d)) {
+            // In browser tree, I can be under any node, But - that
+            // does not mean, it is my parent.
+            //
+            // We have some group nodes too.
+            //
+            // i.e.
+            // Tables, Views, etc. nodes under Schema node
+            //
+            // And, actual parent of a table is schema, not Tables.
+            while (i && t.hasParent(i)) {
+              i = t.parent(i);
+              var pd = t.itemData(i);
+
+              if (isParent(pd)) {
+                // Assign the data, this is my actual parent.
+                d = pd;
+                break;
+              }
+            }
+          }
+
+          // Seriously - I really don't have parent data present?
+          //
+          // The only node - which I know - who does not have parent
+          // node, is the Server Group (and, comes directly under root
+          // node - which has no parent.)
+          if (!d || (this.parent_type != null && !isParent(d))) {
+            // It should never come here.
+            // If it is here, that means - we do have some bug in code.
+            return;
+          }
+
+          if (!d)
+            return;
+
+          l = S( gettext('Create - %s')).sprintf(
+              [this.label]).value();
+          p = addPanel();
+
+          setTimeout(function() {
+            o.showProperties(i, d, p, args.action);
+          }, 10);
+        } else {
+          if (pgBrowser.Node.panels && pgBrowser.Node.panels[d.id] &&
+              pgBrowser.Node.panels[d.id].$container) {
+            var p = pgBrowser.Node.panels[d.id];
+            /**  TODO ::
+             *  Run in edit mode (if asked) only when it is
+             *  not already been running edit mode
+             **/
+            var mode = p.$container.attr('action-mode');
+            if (mode) {
+              var msg = gettext('Are you sure want to stop editing the properties of %s "%s"?');
+              if (args.action == 'edit') {
+                msg = gettext('Are you sure want to reset the current changes and re-open the panel for %s "%s"?');
+              }
+
+              Alertify.confirm(
+                gettext('Edit in progress?'),
+                S(msg).sprintf(o.label.toLowerCase(), d.label).value(),
+                function() {
+                  setTimeout(function() {
+                    o.showProperties(i, d, p, args.action);
+                  }, 10);
+                },
+                null).show();
+            } else {
+              setTimeout(function() {
+                o.showProperties(i, d, p, args.action);
+              }, 10);
+            }
+          } else {
+            pgBrowser.Node.panels = pgBrowser.Node.panels || {};
+            p = pgBrowser.Node.panels[d.id] = addPanel();
+
+            setTimeout(function() {
+              o.showProperties(i, d, p, args.action);
+            }, 10);
+          }
+        }
+
+        p.title(l);
+        p.icon('icon-' + this.type);
+
+        // Make sure the properties dialog is visible
+        p.focus();
+      },
+      // Delete the selected object
+      delete_obj: function(args, item) {
+          var input = args || {'url':'drop'},
+              obj = this,
+              t = pgBrowser.tree,
+              i = input.item || item || t.selected(),
+              d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+        if (!d)
+          return;
+
+        /*
+         * Make sure - we're using the correct version of node
+         */
+        obj = pgBrowser.Nodes[d._type];
+        var objName = d.label;
+
+        var msg, title;
+        if (input.url == 'delete') {
+
+          msg = S( gettext('Are you sure you want to drop %s "%s" and all the objects that depend on it?'))
+            .sprintf(obj.label.toLowerCase(), d.label).value();
+          title = S( gettext('DROP CASCADE %s?')).sprintf(obj.label).value();
+
+          if (!(_.isFunction(obj.canDropCascade) ?
+                obj.canDropCascade.apply(obj, [d, i]) : obj.canDropCascade)) {
+                var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(
+                S('The %s "%s" cannot be dropped!')
+                .sprintf(obj.label, d.label).value(),
+                10
+                );
+            return;
+          }
+        } else {
+          msg = S( gettext('Are you sure you want to drop %s "%s"?'))
+            .sprintf(obj.label.toLowerCase(), d.label).value();
+          title = S( gettext('DROP %s?')).sprintf(obj.label).value();
+
+          if (!(_.isFunction(obj.canDrop) ?
+              obj.canDrop.apply(obj, [d, i]) : obj.canDrop)) {
+            var alertifyWrapper = new AlertifyWrapper();
+            alertifyWrapper.error(
+              S('The %s "%s" cannot be dropped!')
+              .sprintf(obj.label, d.label).value(),
+              10
+            );
+            return;
+          }
+        }
+        Alertify.confirm(title, msg,
+          function() {
+            $.ajax({
+              url: obj.generate_url(i, input.url, d, true),
+              type:'DELETE',
+              success: function(res) {
+                if (res.success == 0) {
+                  pgBrowser.report_error(res.errormsg, res.info);
+                } else {
+                  pgBrowser.removeTreeNode(i, true);
+                }
+                return true;
+              },
+              error: function(jqx) {
+                var msg = jqx.responseText;
+                /* Error from the server */
+                if (jqx.status == 417 || jqx.status == 410 || jqx.status == 500) {
+                  try {
+                    var data = $.parseJSON(jqx.responseText);
+                    msg = data.errormsg;
+                  } catch (e) {}
+                }
+                pgBrowser.report_error(
+                    S( gettext('Error dropping %s: "%s"'))
+                      .sprintf(obj.label, objName)
+                        .value(), msg);
+              }
+            });
+          },
+          null).show()
+      },
+      // Callback for creating script(s) & opening them in Query editor
+      show_script: function(args, item) {
+        var scriptType = args.script,
+          obj = this,
+          t = pgBrowser.tree,
+          i = item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+        if (!d)
+          return;
+
+        /*
+         * Make sure - we're using the correct version of node
+         */
+        obj = pgBrowser.Nodes[d._type];
+        var objName = d.label,
+          sql_url;
+
+        // URL for script type
+        if(scriptType == 'insert') {
+          sql_url = 'insert_sql';
+        } else if(scriptType == 'update') {
+          sql_url = 'update_sql';
+        } else if(scriptType == 'delete') {
+          sql_url = 'delete_sql';
+        } else if(scriptType == 'select') {
+          sql_url = 'select_sql';
+        } else if(scriptType == 'exec') {
+          sql_url = 'exec_sql';
+        } else {
+          // By Default get CREATE SQL
+          sql_url = 'sql';
+        }
+        // Open data grid & pass the URL for fetching
+        pgAdmin.DataGrid.show_query_tool(
+          obj.generate_url(i, sql_url, d, true),
+          i, scriptType
+        );
+      },
+
+      // Callback to render query editor
+      show_query_tool: function(args, item) {
+        var obj = this,
+          t = pgBrowser.tree,
+          i = item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+        if (!d)
+          return;
+
+        // Here call data grid method to render query tool
+        pgAdmin.DataGrid.show_query_tool('', i);
+      },
+      added: function(item, data, browser) {
+        var b = browser || pgBrowser,
+            t = b.tree,
+            pItem = t.parent(item),
+            pData = pItem && t.itemData(pItem),
+            pNode = pData && pgBrowser.Nodes[pData._type];
+
+        // Check node is a collection or not.
+        if (pNode && pNode.is_collection) {
+          /* If 'collection_count' is not present in data
+           * it means tree node expanded first time, so we will
+           * kept collection count and label in data itself.
+           */
+          if (!('collection_count' in pData)) {
+            pData.collection_count = 0;
+          }
+          pData.collection_count++;
+          t.setLabel(
+            pItem, {
+              label: (
+                _.escape(pData._label) + ' <span>(' + pData.collection_count + ')</span>'
+              )
+            }
+          );
+        }
+      },
+      // Callback called - when a node is selected in browser tree.
+      selected: function(item, data, browser) {
+        // Show the information about the selected node in the below panels,
+        // which are visible at this time:
+        // + Properties
+        // + Query (if applicable, otherwise empty)
+        // + Dependents
+        // + Dependencies
+        // + Statistics
+        var b = browser || pgBrowser,
+            t = b.tree,
+            d = data || t.itemData(item);
+
+        // Update the menu items
+        pgAdmin.Browser.enable_disable_menus.apply(b, [item]);
+
+        if (d && b) {
+          if ('properties' in b.panels &&
+              b.panels['properties'] &&
+              b.panels['properties'].panel &&
+              b.panels['properties'].panel.isVisible()) {
+            // Show object properties (only when the 'properties' tab
+            // is active).
+            this.showProperties(item, d, b.panels['properties'].panel);
+          }
+          if ('sql' in b.panels &&
+              b.panels['sql'] &&
+              b.panels['sql'].panel &&
+              b.panels['sql'].panel.isVisible()) {
+            // TODO:: Show reverse engineered query for this object (when 'sql'
+            // tab is active.)
+          }
+          if ('statistics' in b.panels &&
+              b.panels['statistics'] &&
+              b.panels['statistics'].panel &&
+              b.panels['statistics'].panel.isVisible()) {
+            // TODO:: Show statistics for this object (when the 'statistics'
+            // tab is active.)
+          }
+          if ('dependencies' in b.panels &&
+              b.panels['dependencies'] &&
+              b.panels['dependencies'].panel &&
+              b.panels['dependencies'].panel.isVisible()) {
+            // TODO:: Show dependencies for this object (when the
+            // 'dependencies' tab is active.)
+          }
+          if ('dependents' in b.panels &&
+              b.panels['dependents'] &&
+              b.panels['dependents'].panel &&
+              b.panels['dependents'].panel.isVisible()) {
+            // TODO:: Show dependents for this object (when the 'dependents'
+            // tab is active.)
+          }
+        }
+
+        return true;
+      },
+      removed: function(item) {
+        var self = this,
+            t = pgBrowser.tree,
+            pItem = t.parent(item),
+            pData = pItem && t.itemData(pItem),
+            pNode = pData && pgBrowser.Nodes[pData._type];
+
+        // Check node is a collection or not.
+        if (
+          pNode && pNode.is_collection && 'collection_count' in pData
+        ) {
+          pData.collection_count--;
+          t.setLabel(
+            pItem, {
+              label: (
+                _.escape(pData._label) + ' <span>(' + pData.collection_count + ')</span>'
+              )
+            }
+          );
+        }
+
+        setTimeout(function() { self.clear_cache.apply(self, item); }, 0);
+      },
+      unloaded: function(item) {
+        var self = this,
+            t = pgBrowser.tree,
+            data = item && t.itemData(item);
+
+        // In case of unload remove the collection counter
+        if (self.is_collection && 'collection_count' in data)
+        {
+          delete data.collection_count;
+          t.setLabel(item, {label: _.escape(data._label)});
+        }
+      },
+      refresh: function(cmd, i) {
+        var self = this,
+            t = pgBrowser.tree,
+            item = i || t.selected(),
+            d = t.itemData(item);
+
+        pgBrowser.Events.trigger('pgadmin:browser:tree:refresh', item);
+      }
+    },
+    /**********************************************************************
+     * A hook (not a callback) to show object properties in given HTML
+     * element.
+     *
+     * This has been used for the showing, editing properties of the node.
+     * This has also been used for creating a node.
+     **/
+    showProperties: function(item, data, panel, action) {
+      var that = this,
+        tree = pgAdmin.Browser.tree,
+        j = panel.$container.find('.obj_properties').first(),
+        view = j.data('obj-view'),
+        content = $('<div tabindex="1"></div>')
+          .addClass('pg-prop-content col-xs-12');
+
+        // Handle key press events for Cancel, save and help button
+        var 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();
+              break;
+            case keyCode.ENTER:
+              // Return if event is fired from child element
+              if (event.target !== context) return;
+              if (view && view.model && view.model.sessChanged()) {
+                onSave.call(this, view);
+              }
+              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
+
+        // Template function to create the status bar
+        var createStatusBar = function(location){
+            var statusBar = $('<div></div>').addClass(
+                      'pg-prop-status-bar'
+                      ).appendTo(j);
+            statusBar.css("visibility", "hidden");
+            if (location == "header") {
+                statusBar.appendTo(that.header);
+            } else {
+                statusBar.prependTo(that.footer);
+            }
+            that.statusBar = statusBar;
+            return statusBar;
+        }.bind(panel),
+        // Template function to create the button-group
+        createButtons = function(buttons, location, extraClasses) {
+          var panel = this;
+
+          // arguments must be non-zero length array of type
+          // object, which contains following attributes:
+          // label, type, extraClasses, register
+          if (buttons && _.isArray(buttons) && buttons.length > 0) {
+            // All buttons will be created within a single
+            // div area.
+            var btnGroup =
+              $('<div></div>').addClass(
+                  'pg-prop-btn-group'
+                  ),
+              // Template used for creating a button
+              tmpl = _.template([
+                '<button type="<%= type %>" ',
+                'class="btn <%=extraClasses.join(\' \')%>"',
+                '<% if (disabled) { %> disabled="disabled"<% } %> title="<%-tooltip%>">',
+                '<span class="<%= icon %>"></span><% if (label != "") { %>&nbsp;<%-label%><% } %></button>'
+                ].join(' '));
+            if (location == "header"){
+                btnGroup.appendTo(that.header);
+            }else{
+                btnGroup.appendTo(that.footer);
+            }
+            if (extraClasses) {
+              btnGroup.addClass(extraClasses);
+            }
+            _.each(buttons, function(btn) {
+              // Create the actual button, and append to
+              // the group div
+
+              // icon may not present for this button
+              if (!btn.icon) {
+                btn.icon = "";
+              }
+              var b = $(tmpl(btn));
+              btnGroup.append(b);
+              // Register is a callback to set callback
+              // for certain operation for this button.
+              btn.register(b);
+            });
+            return btnGroup;
+          }
+          return null;
+        }.bind(panel),
+        // Callback to show object properties
+        properties = function() {
+
+          // Avoid unnecessary reloads
+          var panel = this,
+              i = tree.selected(),
+              d = i && tree.itemData(i),
+              n_type = d._type,
+              n_value = -1,
+              n = i && d && pgBrowser.Nodes[d._type],
+              treeHierarchy = n.getTreeNodeHierarchy(i);
+
+          if (_.isEqual($(panel).data('node-prop'), treeHierarchy)) {
+            return;
+          }
+
+          // Cache the current IDs for next time
+          $(panel).data('node-prop', treeHierarchy);
+
+          if (!content.hasClass('has-pg-prop-btn-group'))
+            content.addClass('has-pg-prop-btn-group');
+
+          // We need to release any existing view, before
+          // creating new view.
+          if (view) {
+            // Release the view
+            view.remove({data: true, internal: true, silent: true});
+            // Deallocate the view
+            // delete view;
+            view = null;
+            // Reset the data object
+            j.data('obj-view', null);
+          }
+          // Make sure the HTML element is empty.
+          j.empty();
+          that.header = $('<div></div>').addClass(
+                      'pg-prop-header'
+                      ).appendTo(j);
+          that.footer = $('<div></div>').addClass(
+                      'pg-prop-footer'
+                      ).appendTo(j);
+          // Create a view to show the properties in fieldsets
+          view = that.getView(item, 'properties', content, data, 'fieldset', undefined, j);
+          if (view) {
+            // Save it for release it later
+            j.data('obj-view', view);
+
+            // Create proper buttons
+
+            var buttons = [];
+
+            buttons.push({
+              label: '', type: 'edit',
+              tooltip: gettext('Edit'),
+              extraClasses: ['btn-default'],
+              icon: 'fa fa-lg fa-pencil-square-o',
+              disabled: !that.canEdit,
+              register: function(btn) {
+                btn.click(function() {
+                  onEdit();
+                });
+              }
+            });
+
+            buttons.push({
+              label: '', type: 'help',
+              tooltip: gettext('SQL help for this object type.'),
+              extraClasses: ['btn-default', 'pull-right'],
+              icon: 'fa fa-lg fa-info',
+              disabled: (that.sqlAlterHelp == '' && that.sqlCreateHelp == '') ? true : false,
+              register: function(btn) {
+                btn.click(function() {
+                  onSqlHelp();
+                });
+              }
+            });
+
+            createButtons(buttons, 'header', 'pg-prop-btn-group-above bg-gray-2 border-gray-3');
+          }
+          j.append(content);
+        }.bind(panel),
+        onSqlHelp = function() {
+          var panel = this;
+          // See if we can find an existing panel, if not, create one
+          var pnlSqlHelp = pgBrowser.docker.findPanels('pnl_sql_help')[0];
+
+          if (pnlSqlHelp == null) {
+            var pnlProperties = pgBrowser.docker.findPanels('properties')[0];
+            pgBrowser.docker.addPanel('pnl_sql_help', wcDocker.DOCK.STACKED, pnlProperties);
+            pnlSqlHelp = pgBrowser.docker.findPanels('pnl_sql_help')[0];
+          }
+
+          // Construct the URL
+          var server = that.getTreeNodeHierarchy(item).server;
+
+          var url = pgBrowser.utils.pg_help_path;
+          if (server.server_type == 'ppas') {
+            url = pgBrowser.utils.edbas_help_path;
+          }
+
+          var major = Math.floor(server.version / 10000),
+            minor = Math.floor(server.version / 100) - (major * 100);
+
+          url = url.replace('$VERSION$', major + '.' + minor);
+          if (!S(url).endsWith('/')) {
+            url = url + '/'
+          }
+          if (that.sqlCreateHelp == '' && that.sqlAlterHelp != '') {
+              url = url + that.sqlAlterHelp
+          } else if (that.sqlCreateHelp != '' && that.sqlAlterHelp == '') {
+              url = url + that.sqlCreateHelp
+          } else {
+            if (view.model.isNew()) {
+              url = url + that.sqlCreateHelp
+            } else {
+              url = url + that.sqlAlterHelp
+            }
+          }
+
+          // Update the panel
+          var iframe = $(pnlSqlHelp).data('embeddedFrame');
+          pnlSqlHelp.title('SQL: ' + that.label);
+
+          pnlSqlHelp.focus();
+          iframe.openURL(url);
+        }.bind(panel),
+
+        onDialogHelp = function() {
+          var panel = this;
+          // See if we can find an existing panel, if not, create one
+          var pnlDialogHelp = pgBrowser.docker.findPanels('pnl_online_help')[0];
+
+          if (pnlDialogHelp == null) {
+            var pnlProperties = pgBrowser.docker.findPanels('properties')[0];
+            pgBrowser.docker.addPanel('pnl_online_help', wcDocker.DOCK.STACKED, pnlProperties);
+            pnlDialogHelp = pgBrowser.docker.findPanels('pnl_online_help')[0];
+          }
+
+          // Update the panel
+          var iframe = $(pnlDialogHelp).data('embeddedFrame');
+
+          pnlDialogHelp.focus();
+          iframe.openURL(that.dialogHelp);
+        }.bind(panel),
+
+        onSave = function(view) {
+          var m = view.model,
+            d = m.toJSON(true),
+
+            // Generate a timer for the request
+            timer = setTimeout(function(){
+              $('.obj_properties').addClass('show_progress');
+            }, 1000);
+
+          if (d && !_.isEmpty(d)) {
+            m.save({}, {
+              attrs: d,
+              validate: false,
+              cache: false,
+              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
+                var 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) {
+                Alertify.pgNotifier(
+                  "error", jqxhr,
+                  S(
+                    gettext("Error saving properties: %s")
+                    ).sprintf(jqxhr.statusText).value()
+                  );
+
+                // Hide progress cursor
+                $('.obj_properties').removeClass('show_progress');
+                clearTimeout(timer);
+              }
+            });
+          }
+        }.bind(panel),
+
+        editFunc = function() {
+          var panel = this;
+          if (action && action == 'properties') {
+            action = 'edit';
+          }
+          panel.$container.attr('action-mode', action);
+          // We need to release any existing view, before
+          // creating the new view.
+          if (view) {
+            // Release the view
+            view.remove({data: true, internal: true, silent: true});
+            // Deallocate the view
+            view = null;
+            // Reset the data object
+            j.data('obj-view', null);
+          }
+          // Make sure the HTML element is empty.
+          j.empty();
+
+          that.header = $('<div></div>').addClass(
+                      'pg-prop-header'
+                      ).appendTo(j)
+          that.footer = $('<div></div>').addClass(
+                      'pg-prop-footer'
+                      ).appendTo(j);
+
+          var updateButtons = function(hasError, modified) {
+
+            var btnGroup = this.find('.pg-prop-btn-group'),
+                btnSave = btnGroup.find('button.btn-primary'),
+                btnReset = btnGroup.find('button.btn-warning');
+
+            if (hasError || !modified) {
+              btnSave.prop('disabled', true);
+              btnSave.attr('disabled', 'disabled');
+            } else {
+              btnSave.prop('disabled', false);
+              btnSave.removeAttr('disabled');
+            }
+
+            if (!modified) {
+              btnReset.prop('disabled', true);
+              btnReset.attr('disabled', 'disabled');
+            } else {
+              btnReset.prop('disabled', false);
+              btnReset.removeAttr('disabled');
+            }
+          };
+
+          // Create a view to edit/create the properties in fieldsets
+          view = that.getView(item, action, content, data, 'dialog', updateButtons, j, onCancelFunc);
+          if (view) {
+            // Save it to release it later
+            j.data('obj-view', view);
+
+            panel.icon(
+                _.isFunction(that['node_image']) ?
+                  (that['node_image']).apply(that, [data, view.model]) :
+                  (that['node_image'] || ('icon-' + that.type))
+                );
+
+            // Create proper buttons
+            createButtons([{
+              label: '', type: 'help',
+              tooltip: gettext('SQL help for this object type.'),
+              extraClasses: ['btn-default', 'pull-left'],
+              icon: 'fa fa-lg fa-info',
+              disabled: (that.sqlAlterHelp == '' && that.sqlCreateHelp == '') ? true : false,
+              register: function(btn) {
+                btn.click(function() {
+                  onSqlHelp();
+                });
+              }
+            },{
+              label: '', type: 'help',
+              tooltip: gettext('Help for this dialog.'),
+              extraClasses: ['btn-default', 'pull-left'],
+              icon: 'fa fa-lg fa-question',
+              disabled: (that.dialogHelp == '') ? true : false,
+              register: function(btn) {
+                btn.click(function() {
+                  onDialogHelp();
+                });
+              }
+            },{
+              label: gettext('Save'), type: 'save',
+              tooltip: gettext('Save this object.'),
+              extraClasses: ['btn-primary'],
+              icon: 'fa fa-lg fa-save',
+              disabled: true,
+              register: function(btn) {
+                // Save the changes
+                btn.click(function() {
+                  onSave.call(this, view);
+                });
+              }
+            },{
+              label: gettext('Cancel'), type: 'cancel',
+              tooltip: gettext('Cancel changes to this object.'),
+              extraClasses: ['btn-danger'],
+              icon: 'fa fa-lg fa-close',
+              disabled: false,
+              register: function(btn) {
+                btn.click(function() {
+                  // Removing the action-mode
+                  panel.$container.removeAttr('action-mode');
+                  onCancelFunc.call(arguments);
+                });
+              }
+            },{
+              label: gettext('Reset'), type: 'reset',
+              tooltip: gettext('Reset the fields on this dialog.'),
+              extraClasses: ['btn-warning'],
+              icon: 'fa fa-lg fa-recycle',
+              disabled: true,
+              register: function(btn) {
+                btn.click(function() {
+                  setTimeout(function() { editFunc.call(); }, 0);
+                });
+              }
+            }],'footer' ,'pg-prop-btn-group-below bg-gray-2 border-gray-3');
+          };
+
+          // Create status bar.
+          createStatusBar('footer');
+
+          // Add some space, so that - button group does not override the
+          // space
+          content.addClass('pg-prop-has-btn-group-below');
+
+          // Show contents before buttons
+          j.prepend(content);
+        }.bind(panel),
+        closePanel = function() {
+          // Closing this panel
+          this.close();
+        }.bind(panel),
+        updateTreeItem = function(that) {
+          var _old = data,
+              _new = _.clone(view.model.tnode),
+              info = _.clone(view.model.node_info);
+
+          // Clear the cache for this node now.
+          setTimeout(function() { that.clear_cache.apply(that, item); }, 0);
+
+          pgBrowser.Events.trigger(
+            'pgadmin:browser:tree:update',
+            _old, _new, info, {
+              success: function(_item, _newNodeData, _oldNodeData) {
+                pgBrowser.Events.trigger(
+                  'pgadmin:browser:node:updated', _item, _newNodeData,
+                  _oldNodeData
+                );
+                pgBrowser.Events.trigger(
+                  'pgadmin:browser:node:' + _newNodeData._type + ':updated',
+                  _item, _newNodeData, _oldNodeData
+                );
+              }
+            }
+          );
+          closePanel();
+        },
+        saveNewNode = function(that) {
+          var panel = this,
+              j = panel.$container.find('.obj_properties').first(),
+              view = j.data('obj-view');
+
+          // Clear the cache for this node now.
+          setTimeout(function() { that.clear_cache.apply(that, item); }, 0);
+          try {
+            pgBrowser.Events.trigger(
+              'pgadmin:browser:tree:add', _.clone(view.model.tnode),
+              _.clone(view.model.node_info)
+            );
+          } catch (e) {
+            console.log(e);
+          }
+          closePanel();
+        }.bind(panel, that),
+        editInNewPanel = function() {
+          // Open edit in separate panel
+          setTimeout(function() {
+            that.callbacks.show_obj_properties.apply(that, [{
+              'action': 'edit',
+              'item': item
+            }]);
+          }, 0);
+        },
+        onCancelFunc = closePanel,
+        onSaveFunc = updateTreeItem.bind(panel, that),
+        onEdit = editFunc.bind(panel);
+
+      if (action) {
+        if (action == 'create'){
+          onCancelFunc = closePanel;
+          onSaveFunc = saveNewNode;
+        }
+        if (action != 'properties') {
+          // We need to keep track edit/create mode for this panel.
+          editFunc();
+        } else {
+          properties();
+        }
+      } else {
+        /* Show properties */
+        properties();
+        onEdit = editInNewPanel.bind(panel);
+      }
+      if (panel.closeable()) {
+        var onCloseFunc = function() {
+          var j = this.$container.find('.obj_properties').first(),
+              view = j && j.data('obj-view');
+
+          if (view) {
+            view.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) {
+        d = d || t.itemData(i);
+
+        if (_.isString(this.parent_type)) {
+          if (this.parent_type == d._type) {
+            return i;
+          }
+          while(t.hasParent(i)) {
+            i = t.parent(i);
+            d = t.itemData(i);
+
+            if (this.parent_type == d._type)
+              return i;
+          }
+        } else {
+          if (_.indexOf(this.parent_type, d._type) >= 0) {
+            return i;
+          }
+          while(t.hasParent(i)) {
+            i = t.parent(i);
+            d = t.itemData(i);
+
+            if (_.indexOf(this.parent_type, d._type) >= 0)
+              return i;
+          }
+        }
+      }
+      return null;
+    },
+    /**********************************************************************
+     * Generate the URL for different operations
+     *
+     * arguments:
+     *   type:  Create/drop/edit/properties/sql/depends/statistics
+     *   d:     Provide the ItemData for the current item node
+     *   with_id: Required id information at the end?
+     *
+     * Supports url generation for create, drop, edit, properties, sql,
+     * depends, statistics
+     */
+    generate_url: function(item, type, d, with_id, info) {
+      var url = pgBrowser.URL + '{TYPE}/{REDIRECT}{REF}',
+        opURL = {
+          'create': 'obj', 'drop': 'obj', 'edit': 'obj',
+          'properties': 'obj', 'statistics': 'stats'
+        },
+        ref = '', self = this,
+        priority = -Infinity;
+
+      info = (_.isUndefined(item) || _.isNull(item)) ?
+        info || {} : this.getTreeNodeHierarchy(item);
+
+      if (self.parent_type) {
+        if (_.isString(self.parent_type)) {
+          var p = info[self.parent_type];
+          if (p) {
+            priority = p.priority;
+          }
+        } else {
+          _.each(self.parent_type, function(o) {
+            var p = info[o];
+            if (p) {
+              if (priority < p.priority) {
+                priority = p.priority;
+              }
+            }
+          });
+        }
+      }
+
+      _.each(
+        _.sortBy(
+          _.values(
+           _.pick(info,
+            function(v, k, o) {
+              return (v.priority <= priority);
+            })
+           ),
+          function(o) { return o.priority; }
+          ),
+        function(o) {
+          ref = S('%s/%s').sprintf(ref, encodeURIComponent(o._id)).value();
+        });
+
+      ref = S('%s/%s').sprintf(
+          ref, with_id && d._type == self.type ? encodeURIComponent(d._id) : ''
+          ).value();
+
+      var args = {
+        'TYPE': self.type,
+        'REDIRECT': (type in opURL ? opURL[type] : type),
+        'REF': ref
+      };
+
+      return url.replace(/{(\w+)}/g, function(match, arg) {
+        return args[arg];
+      });
+    },
+    // Base class for Node Data Collection
+    Collection: pgBrowser.DataCollection,
+    // Base class for Node Data Model
+    Model: pgBrowser.DataModel,
+    getTreeNodeHierarchy: function(i) {
+      var idx = 0,
+          res = {},
+          t = pgBrowser.tree,
+          d;
+      do {
+        d = t.itemData(i);
+        if (d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId) {
+          res[d._type] = _.extend({}, d, {
+            'priority': idx
+          });
+          idx -= 1;
+        }
+        i = t.hasParent(i) ? t.parent(i) : null;
+      } while (i);
+
+      return res;
+    },
+    cache: function(url, node_info, level, data) {
+      var cached = this.cached = this.cached || {},
+          hash = url,
+          min_priority = (
+              node_info && node_info[level] && node_info[level].priority
+              ) || 0;
+
+      if (node_info) {
+        _.each(
+            _.sortBy(
+              _.values(
+                _.pick(
+                  node_info,
+                  function(v, k, o) {
+                    return (v.priority <= min_priority);
+                  })),
+              function(o) { return o.priority; }),
+            function(o) {
+              hash = S('%s/%s').sprintf(hash, encodeURI(o._id)).value();
+            });
+      }
+
+      if (_.isUndefined(data)) {
+        var res = cached[hash];
+
+        if (!_.isUndefined(res) &&
+            (res.at - Date.now() > 300000)) {
+          res = undefined;
+        }
+        return res;
+      }
+
+      res = cached[hash] = {data: data, at: Date.now(), level: level};
+
+      return res;
+    },
+    clear_cache: function(item) {
+      /*
+       * Reset the cache, when new node is created.
+       *
+       * FIXME:
+       * At the moment, we will clear all the cache for this node. But - we
+       * would like to clear the cache only this nodes parent, so that - it
+       * fetches the new data.
+       */
+      this.cached = {};
+    },
+    cache_level: function(node_info, with_id) {
+      if (node_info) {
+        if (with_id && this.type in node_info) {
+          return this.type;
+        }
+        if (_.isArray(this.parent_type)) {
+          for (var parent in this.parent_type) {
+            if (parent in node_info) {
+              return parent;
+            }
+          }
+          return this.type;
+        }
+        return this.parent_type;
+      }
+    }
+  });
+
+  return pgAdmin.Browser.Node;
+});
diff --git a/web/pgadmin/dashboard/__init__.py b/web/pgadmin/dashboard/__init__.py
index 8ebab16c..23a75c33 100644
--- a/web/pgadmin/dashboard/__init__.py
+++ b/web/pgadmin/dashboard/__init__.py
@@ -10,6 +10,7 @@
 """A blueprint module implementing the dashboard frame."""
 MODULE_NAME = 'dashboard'
 
+import os
 from functools import wraps
 
 from flask import render_template, url_for, Response, g
@@ -36,8 +37,9 @@ class DashboardModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.dashboard',
-            'path': url_for('dashboard.index') + 'dashboard',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/dashboard'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_own_stylesheets(self):
diff --git a/web/pgadmin/dashboard/static/js/dashboard.js b/web/pgadmin/dashboard/static/js/dashboard.js
new file mode 100644
index 00000000..1eee1da1
--- /dev/null
+++ b/web/pgadmin/dashboard/static/js/dashboard.js
@@ -0,0 +1,1120 @@
+define('pgadmin.dashboard', [
+    'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
+    'pgadmin', 'backbone', 'backgrid', 'flotr2', 'alertify',
+    'sources/alerts/alertify_wrapper', 'backgrid.filter',
+    'pgadmin.browser', 'bootstrap', 'wcdocker'
+    ],
+function(url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, Flotr,
+  alertify, AlertifyWrapper) {
+
+  var wcDocker = window.wcDocker,
+  pgBrowser = pgAdmin.Browser;
+
+  /* Return back, this has been called more than once */
+  if (pgAdmin.Dashboard)
+    return;
+
+  var dashboardVisible = true,
+    cancel_query_url = '',
+    is_super_user = false,
+    current_user, maintenance_database,
+    is_server_dashboard = false,
+    is_database_dashboard = false;
+
+  // Custom BackGrid cell, Responsible for cancelling active sessions
+  var cancelQueryCell = Backgrid.Extension.DeleteCell.extend({
+        render: function () {
+          this.$el.empty();
+          this.$el.html(
+            "<i class='fa fa-times-circle' data-toggle='tooltip' " +
+            "title='" + gettext('Cancel the active query') +
+            "'></i>"
+          );
+          this.delegateEvents();
+          return this;
+        },
+        deleteRow: function(e) {
+          var self = this;
+          e.preventDefault();
+
+          var canDeleteRow = Backgrid.callByNeed(
+            self.column.get('canDeleteRow'), self.column, self.model
+          );
+          // If we are not allowed to cancel the query, return from here
+          if(!canDeleteRow)
+            return;
+
+          // This will refresh the grid
+          var refresh_grid = function() {
+            if(is_server_dashboard) {
+              $('#btn_server_activity_refresh').click();
+            } else if(is_database_dashboard) {
+              $('#btn_database_activity_refresh').click();
+            }
+          };
+
+          var title = gettext('Cancel Active Query?'),
+            txtConfirm = gettext('Are you sure you wish to cancel the active query?');
+
+          alertify.confirm(
+            title,
+            txtConfirm,
+            function(evt) {
+              $.ajax({
+                url: cancel_query_url + self.model.get('pid'),
+                type:'DELETE',
+                success: function(res) {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  if (res == gettext('Success')) {
+                    alertifyWrapper.success(gettext('Active query cancelled successfully.'));
+                    refresh_grid();
+                  } else {
+                    alertifyWrapper.error(gettext('An error occurred whilst cancelling the active query.'));
+                  }
+                },
+                error: function(xhr, status, error) {
+                  try {
+                    var err = $.parseJSON(xhr.responseText);
+                    if (err.success == 0) {
+                      var alertifyWrapper = new AlertifyWrapper();
+                      alertifyWrapper.error(err.errormsg);
+                    }
+                  } catch (e) {}
+                }
+              });
+            },
+            function(evt) {
+              return true;
+            }
+          );
+        }
+  });
+
+  pgAdmin.Dashboard = {
+        init: function() {
+            if (this.initialized)
+                return;
+
+            this.initialized = true;
+
+            // Bind the Dashboard object with the 'object_selected' function
+            var selected = this.object_selected.bind(this);
+            var disconnected = this.object_disconnected.bind(this);
+
+            // Listen for selection of any of object
+            pgBrowser.Events.on('pgadmin-browser:tree:selected', selected);
+
+            // Listen for server disconnected event
+            pgBrowser.Events.on('pgadmin:server:disconnect', disconnected);
+
+            // Load the default welcome dashboard
+            var url = url_for('dashboard.index');
+
+            var dashboardPanel = pgBrowser.panels['dashboard'].panel;
+            if (dashboardPanel) {
+                var div = dashboardPanel.layout().scene().find('.pg-panel-content');
+
+                if (div) {
+                    $.ajax({
+                        url: url,
+                        type: "GET",
+                        dataType: "html",
+                        success: function (data) {
+                            $(div).html(data);
+                        },
+                        error: function (xhr, status) {
+                            $(div).html(
+                                '<div class="alert alert-danger pg-panel-message" role="alert">' + gettext('An error occurred whilst loading the dashboard.') + '</div>'
+                            );
+                        }
+                    });
+
+                    // Cache the current IDs for next time
+                    $(dashboardPanel).data('sid', -1)
+                    $(dashboardPanel).data('did', -1)
+                }
+            }
+        },
+
+        // Handle Server Disconnect
+        object_disconnected: function(obj) {
+            this.object_selected(obj.item, obj.data, pgBrowser.Nodes[obj.data._type]);
+        },
+
+        // Handle treeview clicks
+        object_selected: function(item, itemData, node) {
+            if (itemData && itemData._type && dashboardVisible) {
+                var treeHierarchy = node.getTreeNodeHierarchy(item),
+                    url = url_for('dashboard.index'),
+                    sid = -1, did = -1, b = pgAdmin.Browser,
+                    m = b && b.Nodes[itemData._type];
+
+                cancel_query_url = url_for('dashboard.index') + 'cancel_query/';
+
+                // Check if user is super user
+                var server = treeHierarchy['server'];
+                maintenance_database = (server && server.db) || null;
+
+                if(server && server.user && server.user.is_superuser) {
+                  is_super_user = true;
+                } else {
+                  is_super_user = false;
+                  // Set current user
+                  current_user = (server && server.user) ? server.user.name : null;
+                }
+
+                if (m && m.dashboard) {
+                    if (_.isFunction(m.dashboard)) {
+                        url = m.dashboard.apply(
+                            item, itemData, node, treeHierarchy
+                        );
+                    } else {
+                        url = m.dashboard;
+                    }
+                } else {
+                    if ('database' in treeHierarchy) {
+                        sid = treeHierarchy.server._id;
+                        did = treeHierarchy.database._id;
+                        is_server_dashboard = false;
+                        is_database_dashboard = true;
+                        url += sid + '/' + did;
+                        cancel_query_url += sid + '/' + did + '/';
+                    } else if ('server' in treeHierarchy) {
+                        sid = treeHierarchy.server._id;
+                        is_server_dashboard = true;
+                        is_database_dashboard = false;
+                        url += sid;
+                        cancel_query_url += sid + '/';
+                    }
+                }
+
+                var dashboardPanel = pgBrowser.panels['dashboard'].panel;
+                if (dashboardPanel) {
+                    var div = dashboardPanel.layout().scene().find(
+                            '.pg-panel-content'
+                        );
+
+                    if (div) {
+                        if (itemData.connected || _.isUndefined(itemData.connected)) {
+                            // Avoid unnecessary reloads
+                            if (url != $(dashboardPanel).data('dashboard_url') ||
+                               (url == $(dashboardPanel).data('dashboard_url') && $(dashboardPanel).data('server_status') == false )) {
+                                // Clear out everything so any existing timers die off
+                                $(div).empty();
+
+                                $.ajax({
+                                    url: url,
+                                    type: "GET",
+                                    dataType: "html",
+                                    success: function (data) {
+                                        $(div).html(data);
+                                    },
+                                    error: function (xhr, status) {
+                                        $(div).html(
+                                            '<div class="alert alert-danger pg-panel-message" role="alert">' + gettext('An error occurred whilst loading the dashboard.') + '</div>'
+                                        );
+                                    }
+                                });
+                                $(dashboardPanel).data('server_status', true);
+                            }
+
+                        }
+                        else {
+                            $(div).empty();
+                            $(div).html(
+                                '<div class="alert alert-info pg-panel-message" role="alert">' + gettext('Please connect to the selected server to view the dashboard.') + '</div>'
+                            );
+                            $(dashboardPanel).data('server_status', false);
+                        }
+                        // Cache the current IDs for next time
+                        $(dashboardPanel).data('dashboard_url', url);
+
+                    }
+                }
+            }
+        },
+
+        // Render a chart
+        render_chart: function(container, data, dataset, sid, did, url, options, counter, refresh) {
+
+            // Data format:
+            // [
+            //     { data: [[0, y0], [1, y1]...], label: 'Label 1', [options] },
+            //     { data: [[0, y0], [1, y1]...], label: 'Label 2', [options] },
+            //     { data: [[0, y0], [1, y1]...], label: 'Label 3', [options] }
+            // ]
+
+            if (!dashboardVisible)
+              return;
+
+            var y = 0;
+            if (dataset.length == 0) {
+                if (counter == true)
+                {
+                    // Have we stashed initial values?
+                    if (_.isUndefined($(container).data('counter_previous_vals'))) {
+                        $(container).data('counter_previous_vals', data[0])
+                    } else {
+                        // Create the initial data structure
+                        for (var x in data[0]) {
+                            dataset.push({ 'data': [[0, data[0][x] - $(container).data('counter_previous_vals')[x]]], 'label': x });
+                        }
+                    }
+                } else {
+                    // Create the initial data structure
+                    for (var x in data[0]) {
+                        dataset.push({ 'data': [[0, data[0][x]]], 'label': x });
+                    }
+                }
+            } else {
+                for (var x in data[0]) {
+                    // Push new values onto the existing data structure
+                    // If this is a counter stat, we need to subtract the previous value
+                    if (counter == false) {
+                        dataset[y]['data'].unshift([0, data[0][x]]);
+                    } else {
+                        // Store the current value, minus the previous one we stashed.
+                        // It's possible the tab has been reloaded, in which case out previous values are gone
+                        if (_.isUndefined($(container).data('counter_previous_vals')))
+                            return
+
+                        dataset[y]['data'].unshift([0, data[0][x] - $(container).data('counter_previous_vals')[x]]);
+                    }
+
+                    // Reset the time index to get a proper scrolling display
+                    for (var z = 0; z < dataset[y]['data'].length; z++) {
+                        dataset[y]['data'][z][0] = z;
+                    }
+
+                    y++;
+                }
+                $(container).data('counter_previous_vals', data[0])
+            }
+
+             // Remove uneeded elements
+            for (x = 0; x < dataset.length; x++) {
+                // Remove old data points
+                if (dataset[x]['data'].length > 101) {
+                    dataset[x]['data'].pop();
+                }
+            }
+
+            // Draw Graph, if the container still exists and has a size
+            var dashboardPanel = pgBrowser.panels['dashboard'].panel;
+            var div = dashboardPanel.layout().scene().find('.pg-panel-content');
+            if ($(div).find(container).length) { // Exists?
+                if (container.clientHeight > 0 && container.clientWidth > 0) { // Not hidden?
+                    Flotr.draw(container, dataset, options);
+                }
+            } else {
+                return;
+            }
+
+            // Animate
+            var setTimeoutFunc = function () {
+                var path = url + sid;
+                if (did != -1) {
+                    path += '/' + did;
+                }
+                $.ajax({
+                    url: path,
+                    type: "GET",
+                    dataType: "html",
+                    success: function (resp) {
+                        $(container).removeClass('graph-error')
+                        data = JSON.parse(resp);
+                        pgAdmin.Dashboard.render_chart(container, data, dataset, sid, did, url, options, counter, refresh);
+                    },
+                    error: function (xhr, status, msg) {
+                        var err = $.parseJSON(xhr.responseText),
+                            msg = err.errormsg,
+                            cls;
+                        // If we get a 428, it means the server isn't connected
+                        if (xhr.status == 428) {
+                            if (_.isUndefined(msg) || _.isNull(msg)) {
+                              msg = gettext('Please connect to the selected server to view the graph.');
+                            }
+                            cls = 'info';
+                        } else {
+                            msg = gettext('An error occurred whilst rendering the graph.');
+                            cls = 'danger';
+                        }
+
+                        $(container).addClass('graph-error');
+                        $(container).html(
+                            '<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
+                        );
+
+                        // Try again...
+                        if (container.clientHeight > 0 && container.clientWidth > 0) {
+                            setTimeout(setTimeoutFunc, refresh * 1000);
+                        }
+                    },
+                });
+            };
+
+            setTimeout(setTimeoutFunc, refresh * 1000);
+        },
+
+        // Handler function to support the "Add Server" link
+        add_new_server: function() {
+            if (pgBrowser && pgBrowser.tree) {
+                var i = pgBrowser.tree.first(null, false),
+                    serverModule = r('pgadmin.node.server'),
+                    itemData = pgBrowser.tree.itemData(i);
+
+                while (itemData && itemData._type != "server-group") {
+                    i = pgBrowser.tree.next(i);
+                    itemData = pgBrowser.tree.itemData(i);
+                }
+
+                if (!itemData) {
+                    return;
+                }
+
+                if (serverModule) {
+                    serverModule.callbacks.show_obj_properties.apply(
+                        serverModule, [{action: 'create'}, i]
+                    );
+                }
+            }
+        },
+
+        // Render a grid
+        render_grid: function(container, sid, did, url, columns) {
+            var Datum = Backbone.Model.extend({});
+
+            var path = url + sid;
+            if (did != -1) {
+                path += '/' + did;
+            }
+
+            var Data = Backbone.Collection.extend({
+              model: Datum,
+              url: path,
+              mode: "client"
+            });
+
+            var data = new Data();
+
+            // Set up the grid
+            var grid = new Backgrid.Grid({
+              columns: columns,
+              collection: data,
+              className: "backgrid table-bordered presentation table backgrid-striped"
+            });
+
+            // Render the grid
+            $(container).append(grid.render().el)
+
+            // Initialize a client-side filter to filter on the client
+            // mode pageable collection's cache.
+            var filter = new Backgrid.Extension.ClientSideFilter({
+              collection: data
+            });
+
+            // Render the filter
+            $('#' + container.id + '_filter').before(filter.render().el);
+
+            // Add some space to the filter and move it to the right
+            $(filter.el).css({float: "right", margin: "5px", "margin-right": "2px", "margin-top": "3px"});
+
+            // Stash objects for future use
+            $(container).data('data', data);
+            $(container).data('grid', grid);
+            $(container).data('filter', filter);
+        },
+
+        // Render the data in a grid
+        render_grid_data: function(container) {
+            var data = $(container).data('data'),
+                grid = $(container).data('grid'),
+                filter = $(container).data('filter');
+
+            if(_.isUndefined(data)){
+              return null;
+            }
+
+            data.fetch({
+                reset: true,
+                success: function() {
+                    // If we're showing an error, remove it, and replace the grid & filter
+                    if ($(container).hasClass('grid-error')) {
+                        $(container).removeClass('grid-error');
+                        $(container).html(grid.render().el)
+                        $(filter.el).show();
+                    }
+
+                    // Re-apply search criteria
+                    filter.search();
+                },
+                error: function(model, xhr, options) {
+                     var err = $.parseJSON(xhr.responseText),
+                         msg = err.errormsg,
+                         cls;
+                    // If we get a 428, it means the server isn't connected
+                    if (xhr.status == 428) {
+                        if (_.isUndefined(msg) || _.isNull(msg)) {
+                            msg = gettext('Please connect to the selected server to view the table.');
+                        }
+                        cls = 'info';
+                    } else {
+                        msg = gettext('An error occurred whilst rendering the table.');
+                        cls = 'danger';
+                    }
+
+                    // Replace the content with the error, if not already present. Always update the message
+                    if (!$(container).hasClass('grid-error')) {
+                        $(filter.el).hide();
+                        $(container).addClass('grid-error');
+                    }
+
+                    $(container).html(
+                        '<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
+                    );
+
+                    // Try again
+                    setTimeout(function() {
+                        pgAdmin.Dashboard.render_grid_data(container, data);
+                    }, 5000)
+                }
+            });
+        },
+
+        // Rock n' roll on the server dashboard
+        init_server_dashboard: function(sid, version, session_stats_refresh, tps_stats_refresh, ti_stats_refresh, to_stats_refresh, bio_stats_refresh) {
+            var div_sessions = $('.dashboard-container').find('#graph-sessions')[0];
+            var div_tps = $('.dashboard-container').find('#graph-tps')[0];
+            var div_ti = $('.dashboard-container').find('#graph-ti')[0];
+            var div_to = $('.dashboard-container').find('#graph-to')[0];
+            var div_bio = $('.dashboard-container').find('#graph-bio')[0];
+            var div_server_activity = $('.dashboard-container').find('#server_activity');
+            var div_server_locks = $('.dashboard-container').find('#server_locks');
+            var div_server_prepared = $('.dashboard-container').find('#server_prepared');
+            var div_server_config = $('.dashboard-container').find('#server_config');
+            var dataset_sessions = [];
+            var data_sessions = [];
+            var dataset_tps = [];
+            var data_tps = [];
+            var dataset_ti = [];
+            var data_ti = [];
+            var dataset_to = [];
+            var data_to = [];
+            var dataset_bio = [];
+            var data_bio = [];
+
+            // Fake DB ID
+            var did = -1;
+
+            var options_line = {
+                  parseFloat: false,
+                  xaxis: {
+                      min: 100,
+                      max: 0,
+                      autoscale: 0
+                  },
+                  yaxis : {
+                      autoscale: 1
+                  },
+                  legend : {
+                      position : 'nw',
+                      backgroundColor : '#D2E8FF'
+                  }
+            }
+
+            var server_activity_columns = [{
+                name: "pid",
+                label: gettext('PID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "datname",
+                label: gettext('Database'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "usename",
+                label: gettext('User'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "application_name",
+                label: gettext('Application'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "client_addr",
+                label: gettext('Client'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "backend_start",
+                label: gettext('Backend start'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "state",
+                label: gettext('State'),
+                editable: false,
+                cell: "string"
+            }];
+
+            if (version < 90600) {
+                server_activity_columns = server_activity_columns.concat(
+                [{
+                    name: "waiting",
+                    label: gettext('Waiting?'),
+                    editable: false,
+                    cell: "string"
+                }]);
+            } else {
+                server_activity_columns = server_activity_columns.concat(
+                [{
+                    name: "wait_event",
+                    label: gettext('Wait Event'),
+                    editable: false,
+                    cell: "string"
+                },{
+                    name: "blocking_pids",
+                    label: gettext('Blocking PIDs'),
+                    editable: false,
+                    cell: "string"
+                }]);
+            }
+
+            // Add cancel active query button
+            server_activity_columns.unshift({
+              name: "pg-backform-delete", label: "",
+              cell: cancelQueryCell,
+              editable: false, cell_priority: -1,
+              canDeleteRow: pgAdmin.Dashboard.can_cancel_active_query,
+              postgres_version: version
+            });
+
+            var server_locks_columns = [{
+                name: "pid",
+                label: gettext('PID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "datname",
+                label: gettext('Database'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "locktype",
+                label: gettext('Lock type'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "relation",
+                label: gettext('Target relation'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "page",
+                label: gettext('Page'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "tuple",
+                label: gettext('Tuple'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "virtualxid",
+                label: gettext('vXID (target)'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "transactionid",
+                label: gettext('XID (target)'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "classid",
+                label: gettext('Class'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "objid",
+                label: gettext('Object ID'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "virtualtransaction",
+                label: gettext('vXID (owner)'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "mode",
+                label: gettext('Mode'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "granted",
+                label: gettext('Granted?'),
+                editable: false,
+                cell: "string"
+            }];
+
+            var server_prepared_columns = [{
+                name: "git",
+                label: gettext('Name'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "database",
+                label: gettext('Database'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "Owner",
+                label: gettext('Owner'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "transaction",
+                label: gettext('XID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "prepared",
+                label: gettext('Prepared at'),
+                editable: false,
+                cell: "string"
+            }];
+
+            var server_config_columns = [{
+                name: "name",
+                label: gettext('Name'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "category",
+                label: gettext('Category'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "setting",
+                label: gettext('Setting'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "unit",
+                label: gettext('Unit'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "short_desc",
+                label: gettext('Description'),
+                editable: false,
+                cell: "string"
+            }];
+
+            // Render the graphs
+            pgAdmin.Dashboard.render_chart(
+              div_sessions, data_sessions, dataset_sessions, sid, did,
+              url_for('dashboard.session_stats'), options_line, false,
+              session_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_tps, data_tps, dataset_tps, sid, did,
+              url_for('dashboard.tps_stats'), options_line, true,
+              tps_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_ti, data_ti, dataset_ti, sid, did,
+              url_for('dashboard.ti_stats'), options_line, true,
+              ti_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_to, data_to, dataset_to, sid, did,
+              url_for('dashboard.to_stats'), options_line, true,
+              to_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_bio, data_bio, dataset_bio, sid, did,
+              url_for('dashboard.bio_stats'), options_line, true,
+              bio_stats_refresh
+            );
+
+            // Render the tabs, but only get data for the activity tab for now
+            pgAdmin.Dashboard.render_grid(
+              div_server_activity, sid, did,
+              url_for('dashboard.activity'), server_activity_columns
+            );
+            pgAdmin.Dashboard.render_grid(
+              div_server_locks, sid, did, url_for('dashboard.locks'),
+              server_locks_columns
+            );
+            pgAdmin.Dashboard.render_grid(
+              div_server_prepared, sid, did, url_for('dashboard.prepared'),
+              server_prepared_columns
+            );
+            pgAdmin.Dashboard.render_grid(
+              div_server_config, sid, did, url_for('dashboard.config'),
+              server_config_columns
+            );
+
+            pgAdmin.Dashboard.render_grid_data(div_server_activity);
+
+            // (Re)render the appropriate tab
+            $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+                switch ($(e.target).attr('aria-controls')) {
+                    case "tab_server_activity":
+                        pgAdmin.Dashboard.render_grid_data(div_server_activity);
+                        break;
+
+                    case "tab_server_locks":
+                        pgAdmin.Dashboard.render_grid_data(div_server_locks);
+                        break;
+
+                    case "tab_server_prepared":
+                        pgAdmin.Dashboard.render_grid_data(div_server_prepared);
+                        break;
+
+                    case "tab_server_config":
+                        pgAdmin.Dashboard.render_grid_data(div_server_config);
+                        break;
+                }
+            });
+
+            // Handle button clicks
+            $("button").click(function(e){
+                switch(this.id) {
+                    case "btn_server_activity_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_server_activity);
+                        break;
+
+                    case "btn_server_locks_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_server_locks);
+                        break;
+
+                    case "btn_server_prepared_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_server_prepared);
+                        break;
+
+                    case "btn_server_config_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_server_config);
+                        break;
+                }
+            });
+
+        },
+
+        // Rock n' roll on the database dashboard
+        init_database_dashboard: function(sid, did, version, session_stats_refresh, tps_stats_refresh, ti_stats_refresh, to_stats_refresh, bio_stats_refresh) {
+            var div_sessions = document.getElementById('graph-sessions');
+            var div_tps = document.getElementById('graph-tps');
+            var div_ti = document.getElementById('graph-ti');
+            var div_to = document.getElementById('graph-to');
+            var div_bio = document.getElementById('graph-bio');
+            var div_database_activity = document.getElementById('database_activity');
+            var div_database_locks = document.getElementById('database_locks');
+            var div_database_prepared = document.getElementById('database_prepared');
+            var dataset_sessions = [];
+            var data_sessions = [];
+            var dataset_tps = [];
+            var data_tps = [];
+            var dataset_ti = [];
+            var data_ti = [];
+            var dataset_to = [];
+            var data_to = [];
+            var dataset_bio = [];
+            var data_bio = [];
+
+            var options_line = {
+                  parseFloat: false,
+                  xaxis: {
+                      min: 100,
+                      max: 0,
+                      autoscale: 0
+                  },
+                  yaxis : {
+                      autoscale: 1
+                  },
+                  legend : {
+                      position : 'nw',
+                      backgroundColor : '#D2E8FF'
+                  }
+            }
+
+            var database_activity_columns = [{
+                name: "pid",
+                label: gettext('PID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "usename",
+                label: gettext('User'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "application_name",
+                label: gettext('Application'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "client_addr",
+                label: gettext('Client'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "backend_start",
+                label: gettext('Backend start'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "state",
+                label: gettext('State'),
+                editable: false,
+                cell: "string"
+            }];
+
+            if (version < 90600) {
+                database_activity_columns = database_activity_columns.concat(
+                [{
+                    name: "waiting",
+                    label: gettext('Waiting?'),
+                    editable: false,
+                    cell: "string"
+                }]);
+            } else {
+                database_activity_columns = database_activity_columns.concat(
+                [{
+                    name: "wait_event",
+                    label: gettext('Wait Event'),
+                    editable: false,
+                    cell: "string"
+                },{
+                    name: "blocking_pids",
+                    label: gettext('Blocking PIDs'),
+                    editable: false,
+                    cell: "string"
+                }]);
+            }
+
+            database_activity_columns.unshift({
+              name: "pg-backform-delete", label: "",
+              cell: cancelQueryCell,
+              editable: false, cell_priority: -1,
+              canDeleteRow: pgAdmin.Dashboard.can_cancel_active_query,
+              postgres_version: version
+            });
+
+            var database_locks_columns = [{
+                name: "pid",
+                label: gettext('PID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "locktype",
+                label: gettext('Lock type'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "relation",
+                label: gettext('Target relation'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "page",
+                label: gettext('Page'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "tuple",
+                label: gettext('Tuple'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "virtualxid",
+                label: gettext('vXID (target)'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "transactionid",
+                label: gettext('XID (target)'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "classid",
+                label: gettext('Class'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "objid",
+                label: gettext('Object ID'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "virtualtransaction",
+                label: gettext('vXID (owner)'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "mode",
+                label: gettext('Mode'),
+                editable: false,
+                cell: "string"
+            },{
+                name: "granted",
+                label: gettext('Granted?'),
+                editable: false,
+                cell: "string"
+            }];
+
+            var database_prepared_columns = [{
+                name: "git",
+                label: gettext('Name'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "Owner",
+                label: gettext('Owner'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "transaction",
+                label: gettext('XID'),
+                editable: false,
+                cell: "string"
+            }, {
+                name: "prepared",
+                label: gettext('Prepared at'),
+                editable: false,
+                cell: "string"
+            }];
+
+            // Render the graphs
+            pgAdmin.Dashboard.render_chart(
+              div_sessions, data_sessions, dataset_sessions, sid, did,
+              url_for('dashboard.session_stats'), options_line, false,
+              session_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_tps, data_tps, dataset_tps, sid, did,
+              url_for('dashboard.tps_stats'), options_line, true,
+              tps_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_ti, data_ti, dataset_ti, sid, did,
+              url_for('dashboard.ti_stats'), options_line, true,
+              ti_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_to, data_to, dataset_to, sid, did,
+              url_for('dashboard.to_stats'), options_line, true,
+              to_stats_refresh
+            );
+            pgAdmin.Dashboard.render_chart(
+              div_bio, data_bio, dataset_bio, sid, did,
+              url_for('dashboard.bio_stats'), options_line, true,
+              bio_stats_refresh
+            );
+
+            // Render the tabs, but only get data for the activity tab for now
+            pgAdmin.Dashboard.render_grid(
+              div_database_activity, sid, did, url_for('dashboard.activity'),
+              database_activity_columns
+            );
+            pgAdmin.Dashboard.render_grid(
+              div_database_locks, sid, did, url_for('dashboard.locks'),
+              database_locks_columns
+            );
+            pgAdmin.Dashboard.render_grid(
+              div_database_prepared, sid, did, url_for('dashboard.prepared'),
+              database_prepared_columns
+            );
+
+            pgAdmin.Dashboard.render_grid_data(div_database_activity);
+
+            // (Re)render the appropriate tab
+            $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+                switch ($(e.target).attr('aria-controls')) {
+                    case "tab_database_activity":
+                        pgAdmin.Dashboard.render_grid_data(div_database_activity);
+                        break;
+
+                    case "tab_database_locks":
+                        pgAdmin.Dashboard.render_grid_data(div_database_locks);
+                        break;
+
+                    case "tab_database_prepared":
+                        pgAdmin.Dashboard.render_grid_data(div_database_prepared);
+                        break;
+                }
+            });
+
+            // Handle button clicks
+            $("button").click(function(e){
+                switch(this.id) {
+                    case "btn_database_activity_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_database_activity);
+                        break;
+
+                    case "btn_database_locks_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_database_locks);
+                        break;
+
+                    case "btn_database_prepared_refresh":
+                        pgAdmin.Dashboard.render_grid_data(div_database_prepared);
+                        break;
+                }
+            });
+
+        },
+        toggleVisibility: function(flag) {
+          dashboardVisible = flag;
+        },
+        can_cancel_active_query: function(m) {
+          // We will validate if user is allowed to cancel the active query
+          // If there is only one active session means it probably our main
+          // connection session
+          var active_sessions = m.collection.where({'state': 'active'}),
+            alertifyWrapper = new AlertifyWrapper(),
+            pg_version = this.get('postgres_version') || null;
+
+          // With PG10, We have background process showing on dashboard
+          // We will not allow user to cancel them as they will fail with error
+          // anyway, so better usability we will throw our on notification
+
+          // Background processes do not have database field populated
+          if (pg_version && pg_version >= 100000 && !m.get('datname')) {
+            alertifyWrapper.info(
+              gettext('You cannot cancel background worker processes.')
+            );
+            return false;
+          // If it is the last active connection on maintenance db then error out
+          } else if (maintenance_database == m.get('datname') &&
+              m.get('state') == 'active' && active_sessions.length == 1) {
+            alertifyWrapper.error(
+              gettext('You are not allowed to cancel the main active session.')
+            );
+            return false;
+          } else if(m.get('state') == 'idle') {
+            // If this session is already idle then do nothing
+            alertifyWrapper.info(
+              gettext('The session is already in idle state.')
+            );
+            return false;
+          } else if(is_super_user) {
+            // Super user can do anything
+            return true;
+          } else if (current_user && current_user == m.get('usename')) {
+            // Non-super user can cancel only their active queries
+            return true;
+          } else {
+            // Do not allow to cancel someone else session to non-super user
+            var alertifyWrapper = new AlertifyWrapper();
+            alertifyWrapper.error(
+              gettext('Superuser privileges are required to cancel another users query.')
+            );
+            return false;
+          }
+        }
+  };
+
+  return pgAdmin.Dashboard;
+});
diff --git a/web/pgadmin/misc/__init__.py b/web/pgadmin/misc/__init__.py
index 027dfca0..e347d887 100644
--- a/web/pgadmin/misc/__init__.py
+++ b/web/pgadmin/misc/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module providing utility functions for the application."""
 
+import os
 import pgadmin.utils.driver as driver
 from flask import url_for, render_template, Response
 from flask_babel import gettext as _
@@ -24,15 +25,9 @@ class MiscModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.misc.explain',
-            'path': url_for('misc.index') + 'explain/explain',
-            'preloaded': False
-        }, {
-            'name': 'snap.svg',
-            'path': url_for(
-                'misc.static', filename='explain/vendor/snap.svg/' + (
-                    'snap.svg' if config.DEBUG else 'snap.svg-min'
-                )),
-            'preloaded': False
+            'path': os.path.join(self.root_path, 'static/explain/js/explain'),
+            'preloaded': False,
+            'is_template': False
         }]
 
     def get_own_stylesheets(self):
diff --git a/web/pgadmin/misc/bgprocess/__init__.py b/web/pgadmin/misc/bgprocess/__init__.py
index 0f8f3fc7..ca84fd79 100644
--- a/web/pgadmin/misc/bgprocess/__init__.py
+++ b/web/pgadmin/misc/bgprocess/__init__.py
@@ -11,6 +11,8 @@
 A blueprint module providing utility functions for the notify the user about
 the long running background-processes.
 """
+
+import os
 from flask import url_for
 from flask_security import login_required
 from pgadmin.utils import PgAdminModule
@@ -25,8 +27,9 @@ class BGProcessModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.browser.bgprocess',
-            'path': url_for('bgprocess.static', filename='js/bgprocess'),
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/bgprocess'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_own_stylesheets(self):
diff --git a/web/pgadmin/misc/depends/__init__.py b/web/pgadmin/misc/depends/__init__.py
index 30d1c97a..dd697b95 100644
--- a/web/pgadmin/misc/depends/__init__.py
+++ b/web/pgadmin/misc/depends/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module providing utility functions for the application."""
 
+import os
 from flask import url_for
 from pgadmin.utils import PgAdminModule
 
@@ -19,8 +20,9 @@ class DependsModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.browser.object_depends',
-            'path': url_for('depends.static', filename='js/depends'),
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/depends'),
+            'when': None,
+            'is_template': False
         }]
 
 
diff --git a/web/pgadmin/misc/file_manager/__init__.py b/web/pgadmin/misc/file_manager/__init__.py
index 843ec619..ddf292ca 100644
--- a/web/pgadmin/misc/file_manager/__init__.py
+++ b/web/pgadmin/misc/file_manager/__init__.py
@@ -129,8 +129,15 @@ class FileManagerModule(PgAdminModule):
         return [
             {
                 'name': 'pgadmin.file_manager',
-                'path': url_for('file_manager.index') + 'file_manager',
-                'when': None
+                'path': os.path.join(self.root_path, 'static/js/file_manager'),
+                'when': None,
+                'is_template': False
+            },
+            {
+                'name': 'pgadmin.file_utility',
+                'path': os.path.join(self.root_path, 'static/js/utility'),
+                'when': None,
+                'is_template': False
             },
         ]
 
diff --git a/web/pgadmin/misc/file_manager/static/js/file_manager.js b/web/pgadmin/misc/file_manager/static/js/file_manager.js
new file mode 100644
index 00000000..656c3b35
--- /dev/null
+++ b/web/pgadmin/misc/file_manager/static/js/file_manager.js
@@ -0,0 +1,680 @@
+define('misc.file_manager', [
+      'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'alertify',
+      'sources/alerts/alertify_wrapper'
+], function(gettext, url_for, $, _, alertify, AlertifyWrapper) {
+    pgAdmin = pgAdmin || window.pgAdmin || {};
+
+    /*
+     * Hmm... this module is already been initialized, we can refer to the old
+     * object from here.
+     */
+    if (pgAdmin.FileManager) {
+      return pgAdmin.FileManager;
+    }
+
+    pgAdmin.FileManager = {
+      init: function() {
+        if (this.initialized) {
+          return;
+        }
+
+        this.initialized = true;
+
+        // send a request to get transaction id
+        var getTransId = function(configs) {
+          return $.ajax({
+            data: configs,
+            type: "POST",
+            async: false,
+            url: url_for('file_manager.get_trans_id'),
+            dataType: "json",
+            contentType: "application/json; charset=utf-8",
+          });
+        };
+
+        // Function to remove trans id from session
+        var removeTransId = function(trans_id) {
+          return $.ajax({
+            type: "GET",
+            async: false,
+            url: url_for('file_manager.delete_trans_id', {'trans_id': trans_id}),
+            dataType: "json",
+            contentType: "application/json; charset=utf-8",
+          });
+        };
+
+        var set_last_traversed_dir = function(path, trans_id) {
+          return $.ajax({
+            url: url_for('file_manager.save_last_dir', {'trans_id': trans_id}),
+            type: 'POST',
+            data: JSON.stringify(path),
+            contentType: 'application/json'
+          });
+        };
+        // Declare the Storage dialog
+        alertify.dialog('storageManagerDlg', function() {
+          var controls = [], // Keep tracking of all the backform controls
+              // Dialog containter
+              $container = $("<div class='storage_dialog'></div>"),
+              trans_id;
+
+          /*
+           * Function: renderStoragePanel
+           *
+           * Renders the FileManager in the content div based on the given
+           * configuration parameters.
+           */
+          var renderStoragePanel = function(params) {
+            /*
+             * Clear the existing html in the storage content
+             */
+            var content = $container.find('.storage_content');
+            content.empty();
+            $.get(url_for('file_manager.index'), function(data) {
+              content.append(data);
+            });
+
+            var transId = getTransId(params);
+            var t_res;
+            if (transId.readyState == 4) {
+              t_res = JSON.parse(transId.responseText);
+            }
+            trans_id = t_res.data.fileTransId;
+          };
+
+          // Dialog property
+          return {
+            main: function(params) {
+              // Set title and button name
+              var self = this;
+              if (_.isUndefined(params['dialog_title'])) {
+                params['dialog_title'] = 'Storage manager';
+              }
+              this.set('title', params['dialog_title']);
+              if (_.isUndefined(params['btn_primary'])) {
+                params['btn_primary'] = 'Select';
+              }
+              this.set('label', params['btn_primary']);
+
+              params = JSON.stringify(params);
+              $container.find('.storage_content').remove();
+              $container.append("<div class='storage_content'></div>");
+              renderStoragePanel(params);
+              this.elements.dialog.style.minWidth = '630px';
+              this.show();
+              setTimeout(function() {
+                $($container.find('.file_manager')).on('enter-key', function() {
+                    $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+                });
+              }, 200);
+            },
+            settings: {
+              label: undefined
+            },
+            settingUpdated: function (key, oldValue, newValue) {
+              switch (key) {
+              case 'message':
+                  this.setMessage(newValue);
+                  break;
+              case 'label':
+                  if (this.__internal.buttons[0].element) {
+                      this.__internal.buttons[0].element.innerHTML = newValue;
+                  }
+                  break;
+              default:
+                  break;
+              }
+            },
+            setup:function() {
+              return {
+                buttons:[
+                  {
+                    text: gettext("Select"), className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
+                  },
+                  {
+                    text: gettext("Cancel"), className: "btn btn-danger fa fa-times pg-alertify-button"
+                  }
+                ],
+                focus: { element: 0 },
+                options: {
+                  closableByDimmer: false,
+
+                }
+              };
+            },
+            callback: function(closeEvent) {
+              if (closeEvent.button.text == gettext("Select")) {
+                var newFile = $('.storage_dialog #uploader .input-path').val(),
+                    file_data = {'path': $('.currentpath').val()};
+
+                pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:storage_dialog', newFile);
+
+                set_last_traversed_dir(file_data, trans_id);
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+              } else if (closeEvent.button.text == gettext("Cancel")) {
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+              }
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+            },
+            hooks: {
+              onshow: function() {
+                $(this.elements.body).addClass('pgadmin-storage-body');
+              }
+            }
+          };
+        });
+
+        // Declare the Selection dialog
+        alertify.dialog('fileSelectionDlg', function() {
+          var controls = [], // Keep tracking of all the backform controls
+              // Dialog containter
+              $container = $("<div class='storage_dialog file_selection_dlg'></div>");
+          var trans_id;
+
+          // send a request to get transaction id
+          /*
+           * Function: renderStoragePanel
+           *
+           * Renders the FileManager in the content div based on the given
+           * configuration parameters.
+           */
+          var renderStoragePanel = function(configs) {
+            /*
+             * Clear the existing html in the storage content
+             */
+            var content = $container.find('.storage_content');
+            content.empty();
+
+            $.get(url_for('file_manager.index'), function(data) {
+              content.append(data);
+            });
+
+            var transId = getTransId(configs);
+            var t_res;
+            if (transId.readyState == 4) {
+              t_res = JSON.parse(transId.responseText);
+            }
+            trans_id = t_res.data.fileTransId;
+          };
+
+          // Dialog property
+          return {
+            main: function(params) {
+              // Set title and button name
+              var self = this;
+              if (_.isUndefined(params['dialog_title'])) {
+                params['dialog_title'] = 'Select file';
+              }
+              this.set('title', params['dialog_title']);
+              if (_.isUndefined(params['btn_primary'])) {
+                params['btn_primary'] = 'Select';
+              }
+              this.set('label', params['btn_primary']);
+
+              params = JSON.stringify(params);
+              $container.find('.storage_content').remove();
+              $container.append("<div class='storage_content'></div>");
+              renderStoragePanel(params);
+              this.elements.dialog.style.minWidth = '630px';
+              this.show();
+              setTimeout(function() {
+                $($container.find('.file_manager')).on('enter-key', function() {
+                    $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+                });
+              }, 200);
+            },
+            settings: {
+              label: undefined
+            },
+            settingUpdated: function (key, oldValue, newValue) {
+              switch (key) {
+              case 'message':
+                  this.setMessage(newValue);
+                  break;
+              case 'label':
+                  if (this.__internal.buttons[0].element) {
+                      this.__internal.buttons[0].element.innerHTML = newValue;
+                  }
+                  break;
+              default:
+                  break;
+              }
+            },
+            setup:function() {
+              return {
+                buttons:[
+                  {
+                    text: gettext("Select"), key: 13, className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
+                  },
+                  {
+                    text: gettext("Cancel"), key: 27, className: "btn btn-danger fa fa-times pg-alertify-button"
+                  }
+                ],
+                focus: { element: 0 },
+                options: {
+                  closableByDimmer: false,
+                  maximizable: false,
+                  closable: false,
+                  movable: true
+                }
+              };
+            },
+            callback: function(closeEvent) {
+              if (closeEvent.button.text == gettext("Select")) {
+                var newFile = $('.storage_dialog #uploader .input-path').val(),
+                    file_data = {'path': $('.currentpath').val()};
+
+                pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_file', newFile);
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+                // Ajax call to store the last directory visited once user press select button
+
+                set_last_traversed_dir(file_data, trans_id);
+              } else if (closeEvent.button.text == gettext("Cancel")) {
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+              }
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+            },
+            hooks: {
+              onshow: function() {
+                $(this.elements.body).addClass('pgadmin-storage-body');
+              }
+            }
+          };
+        });
+
+        // Declare the Folder Selection dialog
+        alertify.dialog('folderSelectionDlg', function() {
+          var controls = [], // Keep tracking of all the backform controls
+              // Dialog containter
+              $container = $("<div class='storage_dialog folder_selection_dlg'></div>"),
+              trans_id;
+
+          // send a request to get transaction id
+          /*
+           * Function: renderStoragePanel
+           *
+           * Renders the FileManager in the content div based on the given
+           * configuration parameters.
+           */
+          var renderStoragePanel = function(params) {
+            /*
+             * Clear the existing html in the storage content
+             */
+            var content = $container.find('.storage_content');
+            content.empty();
+
+            $.get(url_for('file_manager.index'), function(data) {
+              content.append(data);
+            });
+
+            var transId = getTransId(params);
+            var t_res;
+            if (transId.readyState == 4) {
+              t_res = JSON.parse(transId.responseText);
+            }
+            trans_id = t_res.data.fileTransId;
+          };
+
+          // Dialog property
+          return {
+            main: function(params) {
+              var self = this;
+              // Set title and button name
+              if (_.isUndefined(params['dialog_title'])) {
+                params['dialog_title'] = 'Select folder';
+              }
+              this.set('title', params['dialog_title']);
+              if (_.isUndefined(params['btn_primary'])) {
+                params['btn_primary'] = 'Select';
+              }
+              this.set('label', params['btn_primary']);
+
+              params = JSON.stringify(params);
+              $container.find('.storage_content').remove();
+              $container.append("<div class='storage_content'></div>");
+              renderStoragePanel(params);
+              this.elements.dialog.style.minWidth = '630px';
+              this.show();
+              setTimeout(function() {
+                $($container.find('.file_manager')).on('enter-key', function() {
+                    $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+                });
+              }, 200);
+            },
+            settings: {
+              label: undefined
+            },
+            settingUpdated: function (key, oldValue, newValue) {
+              switch (key) {
+              case 'message':
+                  this.setMessage(newValue);
+                  break;
+              case 'label':
+                  if (this.__internal.buttons[0].element) {
+                      this.__internal.buttons[0].element.innerHTML = newValue;
+                  }
+                  break;
+              default:
+                  break;
+              }
+            },
+            setup:function() {
+              return {
+                buttons:[
+                  {
+                    text: gettext("Select"), key: 13, className: "btn btn-primary fa fa-file file_manager_ok pg-alertify-button disabled"
+                  },
+                  {
+                    text: gettext("Cancel"), key: 27, className: "btn btn-danger fa fa-times pg-alertify-button"
+                  }
+                ],
+                focus: { element: 0 },
+                options: {
+                  closableByDimmer: false,
+                  maximizable: false,
+                  closable: false,
+                  movable: true
+                }
+              };
+            },
+            callback: function(closeEvent) {
+              if (closeEvent.button.text == gettext("Select")) {
+                var newFile = $('.storage_dialog #uploader .input-path').val(),
+                    file_data = {'path': $('.currentpath').val()};
+                pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:select_folder', newFile);
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+                // Ajax call to store the last directory visited once user press select button
+                set_last_traversed_dir(file_data, trans_id);
+              } else if (closeEvent.button.text == gettext("Cancel")) {
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+              }
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+            },
+            hooks: {
+              onshow: function() {
+                $(this.elements.body).addClass('pgadmin-storage-body');
+              }
+            }
+          };
+        });
+
+        // Declare the Create mode dialog
+        alertify.dialog('createModeDlg', function() {
+          var controls = [], // Keep tracking of all the backform controls
+              // Dialog containter
+              $container = $("<div class='storage_dialog create_mode_dlg'></div>"),
+              trans_id;
+
+          /*
+           * Function: renderStoragePanel
+           *
+           * Renders the FileManager in the content div based on the given
+           * configuration parameters.
+           */
+          var renderStoragePanel = function(params) {
+            /*
+             * Clear the existing html in the storage content
+             */
+            var content = $container.find('.storage_content');
+            content.empty();
+
+            $.get(url_for('file_manager.index'), function(data) {
+              content.append(data);
+            });
+
+            var transId = getTransId(params);
+            var t_res;
+            if (transId.readyState == 4) {
+              t_res = JSON.parse(transId.responseText);
+            }
+            trans_id = t_res.data.fileTransId;
+          };
+
+          // Dialog property
+          return {
+            main: function(params) {
+              var self = this,
+                  trans_id;
+              // Set title and button name
+              if (_.isUndefined(params['dialog_title'])) {
+                params['dialog_title'] = 'Create file';
+              }
+              this.set('title', params['dialog_title']);
+              if (_.isUndefined(params['btn_primary'])) {
+                params['btn_primary'] = 'Create';
+              }
+              this.set('label', params['btn_primary']);
+
+              params = JSON.stringify(params);
+              $container.find('.storage_content').remove();
+              $container.append("<div class='storage_content'></div>");
+              renderStoragePanel(params);
+              this.elements.dialog.style.minWidth = '630px';
+              this.show();
+              setTimeout(function() {
+                $($container.find('.file_manager')).on('enter-key', function() {
+                    $($(self.elements.footer).find('.file_manager_ok')).trigger('click')
+                });
+              }, 200);
+            },
+            settings: {
+              label: undefined
+            },
+            settingUpdated: function (key, oldValue, newValue) {
+              switch (key) {
+              case 'message':
+                  this.setMessage(newValue);
+                  break;
+              case 'label':
+                  if (this.__internal.buttons[0].element) {
+                      this.__internal.buttons[0].element.innerHTML = newValue;
+                  }
+                  break;
+              default:
+                  break;
+              }
+            },
+            setup:function() {
+              return {
+                buttons:[
+                  {
+                    text: gettext("Create"), key: 13, className: "btn btn-primary fa fa-file file_manager_create file_manager_ok pg-alertify-button disabled"
+                  },
+                  {
+                    text: gettext("Cancel"), key: 27, className: "btn btn-danger fa fa-times file_manager_create_cancel pg-alertify-button"
+                  }
+                ],
+                focus: { element: 0 },
+                options: {
+                  closableByDimmer: false,
+                  maximizable: false,
+                  closable: false,
+                  movable: true
+                }
+              };
+            },
+            replace_file: function() {
+                var $yesBtn = $('.replace_file .btn_yes'),
+                     $noBtn = $('.replace_file .btn_no');
+
+                $('.storage_dialog #uploader .input-path').attr('disabled', true);
+                $('.file_manager_ok').addClass('disabled');
+                $('.replace_file, .fm_dimmer').show();
+
+                $yesBtn.click(function(e) {
+                  $('.replace_file, .fm_dimmer').hide();
+                  $yesBtn.off();
+                  $noBtn.off();
+                  var newFile = $('.storage_dialog #uploader .input-path').val()
+
+                  pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
+                  $('.file_manager_create_cancel').trigger('click');
+                  $('.storage_dialog #uploader .input-path').attr('disabled', false);
+                  $('.file_manager_ok').removeClass('disabled');
+                });
+
+                $noBtn.click(function(e) {
+                  $('.replace_file, .fm_dimmer').hide();
+                  $yesBtn.off();
+                  $noBtn.off();
+                  $('.storage_dialog #uploader .input-path').attr('disabled', false);
+                  $('.file_manager_ok').removeClass('disabled');
+                });
+            },
+            is_file_exist: function() {
+              var full_path  = $('.storage_dialog #uploader .input-path').val(),
+                  path = full_path.substr(0, full_path.lastIndexOf('/') + 1),
+                  selected_item = full_path.substr(full_path.lastIndexOf('/') + 1),
+                  is_exist = false;
+
+              var file_data = {
+                'path': path,
+                'name': selected_item,
+                'mode': 'is_file_exist'
+              };
+
+              $.ajax({
+                type: 'POST',
+                data: JSON.stringify(file_data),
+                url: url_for('file_manager.filemanager', {'trans_id': trans_id}),
+                dataType: 'json',
+                contentType: "application/x-download; charset=utf-8",
+                async: false,
+                success: function(resp) {
+                  var data = resp.data.result;
+                  if(data['Code'] === 1) {
+                    is_exist = true;
+                  } else {
+                    is_exist = false;
+                  }
+                }
+              });
+              return is_exist;
+            },
+            check_permission: function(path) {
+              var permission = false,
+                  post_data = {
+                    'path': path,
+                    'mode': 'permission'
+                  };
+
+              $.ajax({
+                type: 'POST',
+                data: JSON.stringify(post_data),
+                url: url_for('file_manager.filemanager', {'trans_id': trans_id}),
+                dataType: 'json',
+                contentType: "application/json; charset=utf-8",
+                async: false,
+                success: function(resp) {
+                  var data = resp.data.result;
+                  if (data.Code === 1) {
+                    permission = true;
+                  } else {
+                    $('.file_manager_ok').addClass('disabled');
+                    var alertifyWrapper = new AlertifyWrapper();
+                    alertifyWrapper.error(data.Error);
+                  }
+                },
+                error: function() {
+                  $('.file_manager_ok').addClass('disabled');
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error( gettext('Error occurred while checking access permission.'));
+                }
+              });
+              return permission;
+            },
+            callback: function(closeEvent) {
+              if (closeEvent.button.text == gettext("Create")) {
+                var newFile = $('.storage_dialog #uploader .input-path').val(),
+                    file_data = {'path': $('.currentpath').val()};
+
+                if (!this.check_permission(newFile)) {
+                  closeEvent.cancel = true;
+                  return;
+                }
+
+                if(!_.isUndefined(newFile) && newFile !== '' && this.is_file_exist()) {
+                  this.replace_file();
+                  closeEvent.cancel = true;
+                } else {
+                  pgAdmin.Browser.Events.trigger('pgadmin-storage:finish_btn:create_file', newFile);
+                  var innerbody = $(this.elements.body).find('.storage_content');
+                  $(innerbody).find('*').off();
+                  innerbody.remove();
+                  removeTransId(trans_id);
+                }
+
+                set_last_traversed_dir(file_data, trans_id);
+              } else if (closeEvent.button.text == gettext("Cancel")) {
+                var innerbody = $(this.elements.body).find('.storage_content')
+                $(innerbody).find('*').off();
+                innerbody.remove();
+                removeTransId(trans_id);
+              }
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+            },
+            hooks: {
+              onshow: function() {
+                $(this.elements.body).addClass('pgadmin-storage-body');
+              }
+            }
+          };
+        });
+      },
+      show_storage_dlg: function(params) {
+        alertify.storageManagerDlg(params).resizeTo('60%', '80%');
+      },
+      show_file_selection: function(params) {
+        alertify.fileSelectionDlg(params).resizeTo('60%', '80%');
+      },
+      show_folder_selection: function(params) {
+        alertify.folderSelectionDlg(params).resizeTo('60%', '80%');
+      },
+      show_create_dlg: function(params) {
+        alertify.createModeDlg(params).resizeTo('60%', '80%');
+      },
+      // call dialogs subject to dialog_type param
+      show_dialog: function(params) {
+        if(params.dialog_type == 'select_file') {
+          this.show_file_selection(params);
+        }
+        else if (params.dialog_type == 'select_folder') {
+          this.show_folder_selection(params);
+        }
+        else if (params.dialog_type == 'create_file') {
+          this.show_create_dlg(params);
+        }
+        else {
+          this.show_storage_dlg(params);
+        }
+      }
+    };
+
+    return pgAdmin.FileManager;
+  });
diff --git a/web/pgadmin/misc/file_manager/static/js/utility.js b/web/pgadmin/misc/file_manager/static/js/utility.js
new file mode 100755
index 00000000..4a2754ce
--- /dev/null
+++ b/web/pgadmin/misc/file_manager/static/js/utility.js
@@ -0,0 +1,1663 @@
+/**
+ * Filemanager JS core
+ *
+ * filemanager.js
+ *
+ *  @license  MIT License
+ *  @author Jason Huck - Core Five Labs
+ *  @author Simon Georget <simon (at) linea21 (dot) com>
+ *  @copyright  Authors
+ */
+import loading_icon from 'acitree/image/load-root.gif';
+
+define(['jquery', 'underscore', 'underscore.string',
+  'sources/alerts/alertify_wrapper', 'sources/gettext',
+  'sources/url_for', 'dropzone'
+  ],
+function($, _, S, AlertifyWrapper, gettext, url_for, Dropzone) {
+
+/*---------------------------------------------------------
+  Define functions used for various operations
+---------------------------------------------------------*/
+
+// return file extension
+var getFileExtension = function(name) {
+  var found = name.lastIndexOf('.') + 1;
+  return (found > 0 ? name.substr(found) : "");
+};
+
+// returns filename without extension
+var getFileName = function(name) {
+  var fm_filename = name;
+  if (fm_filename.length > 15 ) {
+    fm_filename = name.substr(0, 10) +'...';
+  }
+  return fm_filename;
+};
+
+/* Common function to load:
+ * en.js language file
+ * file_manager_config.js config file
+ * return transaction id
+ */
+var loadData = function(url) {
+  return $.ajax({
+    async: false,
+    url: url,
+    dataType: 'jsonp',
+    contentType: "application/json; charset=utf-8"
+  });
+};
+
+// Set enable/disable state of list and grid view
+var setViewButtonsFor = function(viewMode) {
+  if (viewMode == 'grid') {
+      $('.grid').addClass('ON');
+      $('.list').removeClass('ON');
+  } else {
+      $('.list').addClass('ON');
+      $('.grid').removeClass('ON');
+  }
+};
+
+var save_file_dialog_view = function(view, trans_id) {
+  return $.ajax({
+    url: url_for('file_manager.save_file_dialog_view', {'trans_id': trans_id}),
+    type: 'POST',
+    async: true,
+    data: JSON.stringify({'view':view}),
+    contentType: 'application/json'
+  });
+};
+
+
+/*
+ * preg_replace
+ */
+var preg_replace = function(array_pattern, array_pattern_replace, str)  {
+  var i = 0, reg_exp, val_to_replace,
+      new_str = String(str);
+
+  while (i < array_pattern.length) {
+    reg_exp= RegExp(array_pattern[i], "g");
+    val_to_replace = array_pattern_replace[i];
+
+    new_str = new_str.replace (reg_exp, val_to_replace);
+    i += 1;
+  }
+
+  return new_str;
+};
+
+ //nameFormat (), separate filename from extension
+var nameFormat = function(input) {
+  var filename = '';
+  if (input.lastIndexOf('.') != -1) {
+    filename  = input.substr(0, input.lastIndexOf('.'));
+    filename += '.' + input.split('.').pop();
+  } else {
+    filename = input;
+  }
+  return filename;
+};
+
+//Converts bytes to kb, mb, or gb as needed for display.
+var formatBytes = function(bytes) {
+  var n = parseFloat(bytes),
+      d = parseFloat(1024),
+      c = 0,
+      lg = pgAdmin.FileUtils.lg,
+      u = [lg.bytes,lg.kb,lg.mb,lg.gb];
+
+  while(true) {
+    if (n < d) {
+      return (Math.round(n * 100) / 100) + u[c];
+    }
+    n /= d;
+    c += 1;
+  }
+};
+
+// Freeze toolbar buttons and display errors
+var handleError = function(errMsg) {
+  $('.storage_dialog .newfile').attr("disabled", "disabled");
+  $('.storage_dialog .upload').attr("disabled", "disabled");
+  $('.storage_dialog .create').attr("disabled", "disabled");
+};
+
+/*
+ * Test if Data structure has the 'cap' capability
+ * 'cap' is one of 'select', 'rename', 'delete', 'download'
+ */
+var has_capability = function(data, cap) {
+  if (typeof(data.Capabilities) == "undefined") {
+    return true;
+  } else {
+    return ($.inArray(cap, data.Capabilities) > -1);
+  }
+};
+
+// return filename extension
+var getExtension = function(filename) {
+  if (filename.split('.').length == 1) {
+    return "";
+  }
+  return filename.split('.').pop();
+};
+
+// return filename without extension
+var getFilename = function(filename) {
+  if (filename.lastIndexOf('.') != -1) {
+    return filename.substring(0, filename.lastIndexOf('.'));
+  } else {
+    return filename;
+  }
+};
+
+/*
+ * Binds specific actions to the toolbar based on capability.
+ * and show/hide buttons
+ */
+var bindToolbar = function(data) {
+
+  // hide/show rename, upload and create button
+  if(_.has(data, 'Capabilities')) {
+    _.each(data.Capabilities, function(cap) {
+      var target_btn = 'button.' + cap,
+          $target_el = $('.file_manager').find(target_btn);
+      if (!has_capability(data, cap) || pgAdmin.FileUtils.hideButtons()) {
+        $target_el.hide();
+      } else {
+        $target_el.show();
+      }
+    });
+  }
+
+  if (!has_capability(data, 'delete') || pgAdmin.FileUtils.hideButtons()) {
+    $('.file_manager').find('button.delete').hide();
+  } else {
+    $('.file_manager').find('button.delete').click(function() {
+      // hide dimmer
+      $('.fileinfo .delete_item, .fm_dimmer').show();
+    });
+
+    // take action based on pressed button yes or no
+    $('.fileinfo .delete_item button.btn_yes').unbind().on('click', function() {
+      var path;
+      if ($('.fileinfo').data('view') == 'grid') {
+        path = decodeURI($('.fileinfo').find('#contents li.selected .clip span').attr('data-alt'));
+        if (path.lastIndexOf('/') == path.length - 1) {
+          data.Path = path;
+          deleteItem(data);
+        } else {
+          deleteItem(data);
+        }
+      } else {
+        path = $('.fileinfo').find('table#contents tbody tr.selected td:first-child').attr('title');
+        if (path.lastIndexOf('/') == path.length - 1) {
+          data.Path = path;
+          deleteItem(data);
+        } else {
+          deleteItem(data);
+        }
+      }
+      // hide dimmer
+      $('.fileinfo .fm_dimmer').hide();
+    });
+
+  }
+
+  // Download file on download button click
+  if (!has_capability(data, 'download') || pgAdmin.FileUtils.hideButtons()) {
+    $('.file_manager').find('button.download').hide();
+  } else {
+    $('.file_manager').find('button.download').unbind().click(function() {
+      var path;
+      if ($('.fileinfo').data('view') == 'grid') {
+        path = $('.fileinfo li.selected').find('.clip span').attr('data-alt');
+        window.open(pgAdmin.FileUtils.fileConnector + '?mode=download&path=' + path, '_blank');
+      } else {
+        path = $('.fileinfo').find('table#contents tbody tr.selected td:first-child').attr('title');
+        window.open(pgAdmin.FileUtils.fileConnector + '?mode=download&path=' + path, '_blank');
+      }
+    });
+  }
+};
+
+// enable/disable button when files/folder are loaded
+var enable_disable_btn = function() {
+  if ($('.fileinfo').data('view') == 'grid') {
+    var $grid_file = $('.file_manager').find('#contents li.selected');
+    $grid_file.removeClass('selected');
+    $('.file_manager').find('button.delete').prop('disabled', true);
+    $('.file_manager').find('button.download').prop('disabled', true);
+    $('.file_manager').find('button.rename').prop('disabled', true);
+    if ($grid_file.length > 0) {
+      $('.file_manager_ok').addClass('disabled');
+    }
+  } else {
+    var $list_file = $('.fileinfo').find('table#contents tbody tr.selected');
+    $list_file.removeClass('selected');
+    $('.file_manager').find('button.delete').prop('disabled', true);
+    $('.file_manager').find('button.download').prop('disabled', true);
+    $('.file_manager').find('button.rename').prop('disabled', true);
+    if ($list_file.length > 0) {
+      $('.file_manager_ok').addClass('disabled');
+    }
+  }
+
+  $('.delete_item').hide();
+  // clear address bar
+  $('.file_manager #uploader .input-path').show();
+  $('.file_manager #uploader .show_selected_file').remove();
+};
+
+/*
+ * Rename the current item and returns the new name.
+ * by double clicking or by clicking the "Rename" button in
+ * table(list) views.
+ */
+var renameItem = function(data) {
+  var orig_name = getFilename(data.Filename),
+      finalName = '',
+      lg = pgAdmin.FileUtils.lg;
+
+  var getNewName = function(rname) {
+    if (rname !== '') {
+      var givenName = nameFormat(rname),
+          suffix = getExtension(data.Filename);
+      if (suffix.length > 0) {
+        givenName = givenName + '.' + suffix;
+      }
+
+      var oldPath = data.Path,
+          post_data = {
+            "mode": "rename",
+            "old": data.Path,
+            "new": givenName
+          };
+
+      $.ajax({
+        type: 'POST',
+        data: JSON.stringify(post_data),
+        url: pgAdmin.FileUtils.fileConnector,
+        dataType: 'json',
+        contentType: "application/json; charset=utf-8",
+        async: false,
+        success: function(resp) {
+          var result = resp.data.result;
+          if (result.Code === 1) {
+            var newPath = result['New Path'],
+                newName = result['New Name'],
+                title = $("#preview h1").attr("title");
+
+            if (typeof title !="undefined" && title == oldPath) {
+              $('#preview h1').text(newName);
+            }
+
+            if ($('.fileinfo').data('view') == 'grid') {
+              $('.fileinfo span[data-alt="' + oldPath + '"]').parent().next('p span').text(newName);
+              $('.fileinfo span[data-alt="' + oldPath + '"]').attr('data-alt', newPath);
+            } else {
+              $('.fileinfo td[title="' + oldPath + '"]').text(newName);
+              $('.fileinfo td[title="' + oldPath + '"]').attr('title', newPath);
+            }
+            $("#preview h1").html(newName);
+
+            // actualized data for binding
+            data.Path=newPath;
+            data.Filename=newName;
+
+            // UnBind toolbar functions.
+            $('.fileinfo').find('button.rename, button.delete, button.download').unbind();
+
+            var alertifyWrapper = new AlertifyWrapper();
+            alertifyWrapper.success(lg.successful_rename);
+          } else {
+            var alertifyWrapper = new AlertifyWrapper();
+            alertifyWrapper.error(result.Error);
+          }
+
+          finalName = result['New Name'];
+        }
+      });
+    }
+  };
+
+  getNewName(data.NewFilename);
+  return finalName;
+};
+
+/*
+ * delete the folder or files by clicking the "Delete" button
+ * in table(list) view
+ */
+var deleteItem = function(data) {
+  var isDeleted = false,
+      lg = pgAdmin.FileUtils.lg,
+      msg = lg.confirmation_delete;
+
+  var doDelete = function(data) {
+    var parent = data.Path.split('/').reverse().slice(1).reverse().join('/') + '/';
+    var post_data = {
+          "mode": "delete",
+          "path": data.Path
+        };
+
+    $.ajax({
+      type: 'POST',
+      data: JSON.stringify(post_data),
+      url: pgAdmin.FileUtils.fileConnector,
+      dataType: 'json',
+      contentType: "application/json; charset=utf-8",
+      async: false,
+      success: function(resp) {
+        var result = resp.data.result;
+        if (result.Code === 1) {
+          isDeleted = true;
+          if (isDeleted) {
+            var alertifyWrapper = new AlertifyWrapper();
+            alertifyWrapper.success(lg.successful_delete);
+            var rootpath = result.Path.substring(0, result.Path.length-1); // removing the last slash
+                rootpath = rootpath.substr(0, rootpath.lastIndexOf('/') + 1);
+            getFolderInfo(rootpath);
+          }
+        } else {
+          isDeleted = false;
+          var alertifyWrapper = new AlertifyWrapper();
+          alertifyWrapper.error(result.Error);
+        }
+      }
+    });
+    return isDeleted;
+  };
+
+  doDelete(data);
+  return isDeleted;
+};
+
+/*---------------------------------------------------------
+  Functions to Retrieve File and Folder Details
+---------------------------------------------------------*/
+/*
+ * Retrieves information about the specified file as a JSON
+ * object and uses that data to populate a template for
+ * list views. Binds the toolbar for that file/folder to
+ * enable specific actions. Called whenever an item is
+ * clicked in list views.
+ */
+var getFileInfo = function(file) {
+  // Update location for status, upload, & new folder functions.
+  pgAdmin.FileUtils.setUploader(file);
+  var capabilities = pgAdmin.FileUtils.data.capabilities;
+
+  // Retrieve the data & populate the template.
+  var d = new Date(), // to prevent IE cache issues
+    is_file_valid =  false;
+  var post_data = {
+    'path': file,
+    'mode': 'getinfo'
+  };
+  var lg = pgAdmin.FileUtils.lg;
+
+  $.ajax({
+    type: 'POST',
+    data: JSON.stringify(post_data),
+    url: pgAdmin.FileUtils.fileConnector,
+    dataType: 'json',
+    contentType: "application/json; charset=utf-8",
+    async: false,
+    success: function(resp) {
+      var data = resp.data.result;
+      if (data.Code === 1) {
+        $('.file_manager_ok').removeClass('disabled');
+        var properties = '';
+        if (
+          data.Properties.Size || parseInt(data.Properties.Size)==0
+        ) {
+          properties += '<dt>' + lg.size + '</dt><dd>' + formatBytes(
+            data.Properties.Size
+          ) + '</dd>';
+        }
+        data.Capabilities = capabilities;
+        bindToolbar(data);
+        if (data.FileType == 'Directory') {
+          // Enable/Disable level up button
+          enab_dis_level_up();
+          $('.file_manager_ok').addClass('disabled');
+
+          $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+          $('.file_manager button.download').attr('disabled', 'disabled');
+
+          if (file.charAt(file.length - 1) != '/' && file.charAt(file.length - 1) != '\\') {
+            file += '/';
+          }
+          getFolderInfo(file);
+        } else {
+          is_file_valid = true;
+        }
+      } else {
+        $('.file_manager_ok').addClass('disabled');
+        var alertifyWrapper = new AlertifyWrapper();
+        alertifyWrapper.error(data.Error);
+      }
+    }
+  });
+  return is_file_valid;
+};
+
+var checkPermission = function(path) {
+  var permission = false,
+      post_data = {
+        'path': path,
+        'mode': 'permission'
+      };
+
+  $.ajax({
+    type: 'POST',
+    data: JSON.stringify(post_data),
+    url: pgAdmin.FileUtils.fileConnector,
+    dataType: 'json',
+    contentType: "application/json; charset=utf-8",
+    async: false,
+    success: function(resp) {
+      var data = resp.data.result;
+      if (data.Code === 1) {
+        permission = true;
+      } else {
+        $('.file_manager_ok').addClass('disabled');
+        var alertifyWrapper = new AlertifyWrapper();
+        alertifyWrapper.error(data.Error);
+      }
+    },
+    error: function() {
+      $('.file_manager_ok').addClass('disabled');
+      var alertifyWrapper = new AlertifyWrapper();
+      alertifyWrapper.error( gettext('Error occurred while checking access permission.'));
+    }
+  });
+  return permission;
+};
+
+
+
+/*
+ * Retrieves data for all items within the given folder and
+ * creates a list view.
+ */
+var getFolderInfo = function(path, file_type) {
+  $('.storage_dialog #uploader .input-path').prop('disabled', true);
+  if (!file_type) {
+    file_type = '';
+  }
+  var capabilities = pgAdmin.FileUtils.data.Capabilities;
+  // Update location for status, upload, & new folder functions.
+  pgAdmin.FileUtils.setUploader(path);
+
+  // set default selected file type
+  if (file_type === '') {
+    file_type = $('.change_file_types select').val();
+  }
+
+  // navigate to directory or path when clicked on breadcrumbs
+  $('.file_manager a.breadcrumbs').unbind().on('click', function() {
+    var curr_path = $(this).attr('data-path'),
+        current_dir = $(this).html(),
+        move_to = curr_path.substring(
+          0, curr_path.lastIndexOf(current_dir)
+        ) + current_dir;
+
+    getFolderInfo(move_to);
+    enab_dis_level_up();
+  });
+
+   // hide select file if we are listing drives in windows.
+   if (pgAdmin.FileUtils.hideButtons()) {
+     $(".allowed_file_types .change_file_types").hide();
+   } else {
+     $(".allowed_file_types .change_file_types").show();
+   }
+
+  var loading_icon_url = url_for(
+    'static', {'filename': 'js/generated/' + loading_icon }
+  );
+
+  // Display an activity indicator.
+  $('.fileinfo').find('span.activity').html(
+    '<img src="' + loading_icon_url + '"/>'
+  );
+
+  var post_data = {
+    'path': path,
+    'mode': 'getfolder',
+    'file_type': file_type || "*"
+  };
+
+  var lg = pgAdmin.FileUtils.lg;
+  $.ajax({
+    type: 'POST',
+    data: JSON.stringify(post_data),
+    url: pgAdmin.FileUtils.fileConnector,
+    dataType: 'json',
+    contentType: "application/json; charset=utf-8",
+    async: false,
+    success: function(resp) {
+      $('.storage_dialog #uploader .input-path').prop('disabled', false);
+      var result = '',
+          data = resp.data.result;
+
+      // hide activity indicator
+      $('.fileinfo').find('span.activity').hide();
+      if (data.Code === 0) {
+        var alertifyWrapper = new AlertifyWrapper();
+        alertifyWrapper.error(data.Error);
+        return;
+      }
+      // generate HTML for files/folder and render into container
+      if (!_.isEmpty(data)) {
+        if ($('.fileinfo').data('view') == 'grid') {
+          result += '<ul id="contents" class="grid">';
+          Object.keys(data).sort(function keyOrder(x, y) {
+              return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase());
+            }).forEach(function (key) {
+            var props = (data[key]).Properties,
+                cap_classes = "";
+
+            Object.keys(capabilities).forEach(function (cap) {
+              if (has_capability(data[key], capabilities[cap])) {
+                cap_classes += "cap_" + capabilities[cap];
+              }
+            });
+
+            (data[key]).Capabilities = capabilities;
+            bindToolbar(data[key]);
+
+            var class_type;
+            if ((data[key]).file_type == 'dir') {
+              class_type = 'fa fa-folder-open fm_folder';
+            } else if ((data[key]).file_type == 'drive') {
+              class_type = 'fa fa-hdd-o fm_drive';
+            } else {
+              class_type = 'fa fa-file-text fm_file';
+            }
+
+            var fm_filename = (data[key]).Filename;
+            if (fm_filename.length > 15 ) {
+              fm_filename = (data[key]).Filename.substr(0, 10) +'...';
+            }
+
+            var file_name_original = encodeURI((data[key]).Filename);
+            var file_path_orig = encodeURI((data[key]).Path);
+
+            result += '<li class="' + cap_classes +
+              '"><div class="clip"><span data-alt="' +
+                file_path_orig + '" class="' + class_type + '"></span>';
+            if ((data[key]).Protected == 1) {
+              result += '<span class="fa fa-lock fm_lock_icon" data-protected="protected"></span>';
+            }
+
+            result += '</div>';
+            if (!has_capability(data[key], 'rename')) {
+              result += '<span>' + fm_filename + '</span>';
+            } else {
+              result +=
+                '<p><input type="text" class="fm_file_rename" />' +
+                '<span class="less_text" title="' + file_name_original+'">' + fm_filename +
+                '</span></p>';
+            }
+            if (props.Width && props.Width != '') {
+               result += '<span class="meta dimensions">' +
+                 props.Width + 'x' + props.Height + '</span>';
+            }
+            if (props.Size && props.Size != '') {
+              result += '<span class="meta size">' +
+                props.Size + '</span>';
+            }
+            if (props['Date Created'] && props['Date Created'] != '') {
+              result += '<span class="meta created">' +
+                props['Date Created'] + '</span>';
+            }
+            if (props['Date Modified'] && props['Date Modified'] != '') {
+              result += '<span class="meta modified">' +
+                props['Date Modified'] + '</span>';
+            }
+            result += '</li>';
+          });
+
+          result += '</ul>';
+        } else {
+          result += '<table id="contents" class="list">';
+          result += '<thead><tr><th class="headerSortDown">';
+          result += '<span>' + lg.name +'</span></th>';
+          result += '<th><span>' + lg.size + '</span></th><th>';
+          result += '<span>' + lg.modified + '</span></th></tr></thead>';
+          result += '<tbody>';
+
+          Object.keys(data).sort(function keyOrder(x, y) {
+              return pgAdmin.natural_sort(x.toLowerCase(), y.toLowerCase());
+            }).forEach(function (key) {
+            var path = encodeURI((data[key]).Path),
+                props = (data[key]).Properties,
+                cap_classes = "", cap, class_type;
+
+            for (cap in capabilities) {
+              if (has_capability(data[key], capabilities[cap])) {
+                cap_classes += " cap_" + capabilities[cap];
+              }
+            }
+
+            (data[key]).Capabilities = capabilities;
+            bindToolbar(data[key]);
+
+            if ((data[key]).file_type == 'dir') {
+              class_type = 'fa fa-folder-open tbl_folder';
+            }
+            else if ((data[key]).file_type == 'drive') {
+              class_type = 'fa fa-hdd-o tbl_drive';
+            } else {
+              class_type = 'fa fa-file-text tbl_file';
+            }
+
+            var file_name_original = encodeURI((data[key]).Filename);
+            result += '<tr class="' + cap_classes + '">';
+
+            var fm_filename = (data[key]).Filename;
+            if (fm_filename.length > 48) {
+              fm_filename = (data[key]).Filename.substr(0, 48) +'...';
+            }
+
+            result += '<td title="' + path + '" class="'+class_type+'">';
+            if ((data[key]).Protected == 1) {
+              result +=
+                '<i class="fa fa-lock tbl_lock_icon" data-protected="protected"></i>';
+            }
+            if (!has_capability(data[key], 'rename')) {
+              result += '<span title="' + (data[key]).Filename + '">' +
+                fm_filename + '</span></td>';
+            } else {
+              result += '<p><input type="text" class="fm_file_rename"/><span class="less_text" title="' +
+                file_name_original + '">' + fm_filename + '</span></p></td>';
+            }
+            if (props.Size && props.Size != '') {
+              result += '<td><span title="' + props.Size + '">' +
+                props.Size + '</span></td>';
+            } else {
+              result += '<td></td>';
+            }
+
+            if (props['Date Modified'] && props['Date Modified'] != '') {
+              result += '<td>' + props['Date Modified'] + '</td>';
+            } else {
+              result += '<td></td>';
+            }
+
+            result += '</tr>';
+          });
+
+          result += '</tbody>';
+          result += '</table>';
+        }
+      } else {
+        if ($('.fileinfo').data('view') == 'grid') {
+          result += '<ul id="contents" class="grid"></ul>';
+        } else {
+          result += '<table id="contents" class="list">';
+          result += '<thead><tr><th class="headerSortDown"><span>' +
+            lg.name + '</span></th><th><span>' + lg.size +
+              '</span></th><th><span>' + lg.modified +
+              '</span></th></tr></thead>';
+          result += '<tbody>';
+        }
+        result += '<h1 class="no_folder_found">' +
+          lg.could_not_retrieve_folder + '</h1>';
+
+        var cap_no_folders = ['upload', 'create'];
+
+        data.Capabilities = cap_no_folders;
+        bindToolbar(data);
+      }
+
+      // Add the new markup to the DOM.
+      $('.fileinfo .file_listing').html(result);
+
+      // rename file/folder
+      $('.file_manager button.rename').unbind().on('click',function(e) {
+        if ($('.fileinfo').data('view') == 'grid') {
+          e.stopPropagation();
+          var $this = $('.file_manager').find('#contents li.selected p'),
+              orig_value = decodeURI($this.find('span').attr('title')),
+              newvalue = orig_value.substring(0, orig_value.indexOf('.'));
+
+          if (newvalue === '') {
+            newvalue = decodeURI(orig_value);
+          }
+
+          $this.find('input').toggle().val(newvalue).focus();
+          $this.find('span').toggle();
+
+          // Rename folder/file on pressing enter key
+          $('.file_manager').unbind().on('keyup', function(e) {
+            if (e.keyCode == 13) {
+              e.stopPropagation();
+              $('.fileinfo #contents li.selected p').find(
+                'input'
+              ).trigger('blur');
+            }
+          });
+        } else if ($('.fileinfo').data('view') == 'list') {
+          e.stopPropagation();
+          var $this = $('.fileinfo').find(
+                'table#contents tbody tr.selected td:first-child p'
+              ),
+              orig_value = decodeURI($this.find('span').html()),
+              newvalue = orig_value.substring(0, orig_value.lastIndexOf('.'));
+
+          if (orig_value.lastIndexOf('/') == orig_value.length - 1 || newvalue === '') {
+            newvalue = decodeURI(orig_value);
+          }
+
+          $this.find('input').toggle().val(newvalue).focus();
+          $this.find('span').toggle();
+
+          // Rename folder/file on pressing enter key
+          $('.file_manager').unbind().on('keyup', function(e) {
+            if (e.keyCode == 13) {
+              e.stopPropagation();
+              $('.fileinfo table#contents tr.selected td p').find(
+                'input'
+              ).trigger('blur');
+            }
+          });
+        }
+      });
+
+      // Rename UI handling
+      $('.fileinfo #contents li p').on('blur dblclick','input', function(e) {
+        e.stopPropagation();
+
+        var old_name = decodeURI($(this).siblings('span').attr('title')),
+            newvalue = old_name.substring(0, old_name.indexOf('.')),
+            last = getFileExtension(old_name);
+
+        if (old_name.indexOf('.') == 0) {
+          last = '';
+        }
+
+        if (newvalue == '') {
+          newvalue = decodeURI(old_name);
+        }
+
+        if (e.type=="keydown") {
+          if (e.which==13) {
+            var full_name = decodeURI($(this).val()) + (
+              last !== '' ? '.' + last: ''
+            );
+
+            $(this).toggle();
+            $(this).siblings('span').toggle().html(full_name);
+
+            var new_name = decodeURI($(this).val()),
+                path = decodeURI($(this).parent().parent().find(
+                  'span'
+                ).attr('data-alt')),
+                data = {
+                  'Filename': old_name,
+                  'Path': path,
+                  'NewFilename': new_name
+                };
+
+            if (newvalue !== new_name) {
+              renameItem(data);
+              var parent = $('.currentpath').val();
+              getFolderInfo(parent);
+            }
+            e.stopPropagation();
+          }
+
+          if (
+            e.which==38 || e.which==40 || e.which==37 ||
+            e.which==39 || e.keyCode == 32
+          ) {
+              e.stopPropagation();
+          }
+        } else if (e.type=="focusout") {
+          if ($(this).css('display')=="inline-block" || $(this).css('display')=="inline") {
+            var full_name = decodeURI(
+                  $(this).val()
+                ) + (last !== ''? '.' + last: '');
+
+            $(this).toggle();
+            $(this).siblings('span').toggle().html(full_name);
+
+            var new_name = decodeURI($(this).val()),
+                path = decodeURI($(this).parent().parent().find(
+                  'span'
+                ).attr('data-alt')),
+                data = {
+                  'Filename': old_name,
+                  'Path': path,
+                  'NewFilename': new_name
+                };
+
+            if (newvalue !== new_name) {
+              renameItem(data);
+              getFolderInfo($('.currentpath').val());
+            }
+          }
+        } else {
+          e.stopPropagation();
+        }
+      });
+
+      $('.fileinfo table#contents tr td p').on(
+        'blur dblclick', 'input', function(e) {
+        var old_name = decodeURI($(this).siblings('span').attr('title')),
+            newvalue = old_name.substring(0, old_name.indexOf('.')),
+            last = getFileExtension(old_name);
+        if (old_name.indexOf('.') == 0) {
+          last = '';
+        }
+
+        if (newvalue == '') {
+          newvalue = old_name;
+        }
+
+        if (e.type=="focusout") {
+          if ($(this).css('display')=="inline-block" || $(this).css('display')=="inline") {
+            var full_name = decodeURI($(this).val()) + (
+              last !== ''? '.' + last: ''
+            );
+            $(this).toggle();
+            $(this).siblings('span').toggle().html(full_name);
+
+            var new_name = decodeURI($(this).val()),
+                path = decodeURI($(this).parent().parent().attr('title')),
+                data = {
+                  'Filename': old_name,
+                  'Path': path,
+                  'NewFilename': new_name
+                };
+
+            if (newvalue !== new_name) {
+              renameItem(data);
+              var parent = path.split('/').reverse().slice(2).reverse().join('/') + '/';
+              getFolderInfo(parent);
+            }
+          }
+        } else {
+          e.stopPropagation();
+        }
+      });
+
+      var data_cap = {};
+      data_cap.Capabilities = capabilities;
+      /*
+       * Bind click events
+       * Select items - afolder dblclick
+       */
+      if ($('.fileinfo').data('view') == 'grid') {
+        // Get into folder on dblclick
+        $('.fileinfo').find('#contents li').dblclick(function(e) {
+          e.stopPropagation();
+          // Enable/Disable level up button
+          enab_dis_level_up();
+
+          var path = decodeURI($(this).find('span').attr('data-alt'));
+
+          if (path.lastIndexOf("/") == path.length - 1 || path.lastIndexOf("\\") == path.length - 1) {
+            $('.file_manager_ok').addClass('disabled');
+
+            $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+            $('.file_manager button.download').attr('disabled', 'disabled');
+
+            getFolderInfo(path);
+
+          } else {
+            getFileInfo(path);
+          }
+        });
+
+        $('.fileinfo').find('#contents li').click(function(e) {
+          e.stopPropagation();
+          var path = decodeURI($(this).find('.clip span').attr('data-alt')),
+              file_name = $(this).find('p span').attr('title'),
+              is_protected = $(this).find(
+                '.clip span.fm_lock_icon'
+              ).attr('data-protected');
+
+          if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+            if (
+              has_capability(data_cap, 'select_folder') &&
+              is_protected == undefined
+            ) {
+              $(this).parent().find('li.selected').removeClass('selected');
+              $(this).addClass('selected');
+
+              $('.file_manager_ok').removeClass('disabled');
+              $('.file_manager button.delete, .file_manager button.rename').removeAttr(
+                'disabled', 'disabled'
+              );
+              $('.file_manager button.download').attr(
+                'disabled', 'disabled'
+              );
+              // set selected folder name in breadcrums
+              $('.file_manager #uploader .input-path').hide();
+              $('.file_manager #uploader .show_selected_file').remove();
+              $('<span class="show_selected_file">'+path+'</span>').appendTo(
+                '.file_manager #uploader .filemanager-path-group'
+              );
+            }
+          } else {
+            if (
+              has_capability(data_cap, 'select_file') &&
+              is_protected == undefined
+            ) {
+              $(this).parent().find('li.selected').removeClass('selected');
+              $(this).addClass('selected');
+              $('.file_manager_ok').removeClass('disabled');
+              $('.file_manager button.delete, .file_manager button.download, .file_manager button.rename').removeAttr(
+                'disabled'
+              );
+              // set selected folder name in breadcrums
+              $('.file_manager #uploader .show_selected_file').remove();
+            }
+
+            getFileInfo(path);
+          }
+        });
+      } else {
+        $('.fileinfo table#contents tbody tr').on('click', function(e) {
+          e.stopPropagation();
+          var path = decodeURI($('td:first-child', this).attr('title')),
+              file_name = decodeURI($('td:first-child p span', this).attr(
+                'title'
+              )),
+              is_protected = $('td:first-child', this).find(
+                'i.tbl_lock_icon'
+              ).attr('data-protected');
+
+          if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+            if (has_capability(data_cap, 'select_folder') && is_protected == undefined) {
+              $(this).parent().find('tr.selected').removeClass('selected');
+              $('td:first-child', this).parent().addClass('selected');
+              $('.file_manager_ok').removeClass('disabled');
+              $('.file_manager button.download').attr('disabled', 'disabled');
+              $('.file_manager button.delete, .file_manager button.rename').removeAttr('disabled');
+
+              // set selected folder name in breadcrums
+              $('.file_manager #uploader .input-path').hide();
+              $('.file_manager #uploader .show_selected_file').remove();
+              $('<span class="show_selected_file">'+path+'</span>').appendTo(
+                '.file_manager #uploader .filemanager-path-group'
+              );
+            }
+          } else {
+            if (has_capability(data_cap, 'select_file') && is_protected == undefined) {
+              $(this).parent().find('tr.selected').removeClass('selected');
+              $('td:first-child', this).parent().addClass('selected');
+              $('.file_manager button.delete, .file_manager button.download, .file_manager button.rename').removeAttr(
+                'disabled'
+              );
+              // set selected folder name in breadcrums
+              $('.file_manager #uploader .show_selected_file').remove();
+            }
+
+            getFileInfo(path);
+          }
+        });
+
+        $('.fileinfo table#contents tbody tr').on('dblclick', function(e) {
+          e.stopPropagation();
+          // Enable/Disable level up button
+          enab_dis_level_up();
+          var path = $('td:first-child', this).attr('title');
+
+          if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+            $('.file_manager_ok').removeClass('disabled');
+            $('.file_manager button.download').attr('disabled', 'disabled');
+            $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+            getFolderInfo(path);
+          } else {
+            getFileInfo(path);
+          }
+        });
+
+      }
+      //input_object.set_cap(data_cap);
+    },
+    error: function() {
+      $('.storage_dialog #uploader .input-path').prop('disabled', false);
+    }
+  });
+};
+
+// Enable/Disable level up button
+var enab_dis_level_up = function() {
+  $('.file_manager #uploader .input-path').show();
+  $('.show_selected_file').remove();
+
+  setTimeout(function() {
+    var b = $('.currentpath').val(),
+        $level_up = $('.file_manager').find('button.level-up'),
+        $home_btn = $('.file_manager').find('button.home');
+
+    if (b === '/') {
+      $level_up.attr('disabled', 'disabled');
+      $home_btn.attr('disabled', 'disabled');
+    } else {
+      $home_btn.removeAttr('disabled');
+      $level_up.removeAttr('disabled');
+    }
+  }, 100);
+};
+
+/*---------------------------------------------------------
+  Initialization - Entry point
+---------------------------------------------------------*/
+/*
+ * get transaction id to generate request url and
+ * to generate config files on runtime
+ */
+pgAdmin.FileUtils = {
+  init: function() {
+    var fm_url = url_for('file_manager.get_trans_id'),
+        transId = loadData(fm_url),
+        t_res,
+        t_id;
+
+    if (transId.readyState == 4) {
+      t_res = JSON.parse(transId.responseText);
+    }
+    t_id = t_res.data.fileTransId;
+    var root_url = url_for("file_manager.index"),
+        file_manager_config_json = root_url+t_id+'/file_manager_config.json',
+        file_manager_config_js = root_url+'file_manager_config.js',
+        fileConnector = root_url+'filemanager/'+t_id+'/',
+        cfg = loadData(file_manager_config_json),
+        config;
+
+    this.fileConnector = fileConnector;
+    this.transId = t_id;
+    // load user configuration file
+    if (cfg.readyState == 4) {
+      this.config = config = JSON.parse(cfg.responseText);
+    }
+
+    // set main url to filemanager and its capabilites
+    var fileRoot = config.options.fileRoot,
+        capabilities = config.options.capabilities;
+
+    /*
+     * Get localized messages from file
+     * through culture var or from URL
+     */
+
+    var lg = [],
+        enjs = url_for("file_manager.index") + "en.js",
+        lgf = loadData(enjs);
+
+    if (lgf.readyState == 4) {
+      this.lg = lg = JSON.parse(lgf.responseText);
+    }
+
+    // create and enable user to create new file
+    if (
+      config.options.dialog_type == 'select_file' ||
+      config.options.dialog_type == 'create_file' ||
+      config.options.dialog_type == 'storage_dialog'
+    ) {
+      // Create file selection dropdown
+      var allowed_types = config.options.allowed_file_types,
+          types_len = allowed_types.length,
+          select_box = '',
+          file_type = '';
+
+      if (types_len > 0) {
+        var i = 0, j = 0, t,
+            selected = false,
+            have_all_types = false;
+
+        var select_box = "<div class='change_file_types'>" +
+          "<select name='type'>";
+
+        while(i < types_len) {
+          t = allowed_types[i];
+          if (!selected && (types_len == 1 || t != '*')) {
+            select_box += "<option value=" + t + " selected>" + t + "</option>";
+            selected = true;
+            have_all_types = (have_all_types || (t == '*'));
+          } else {
+            select_box += '<option value="' + t +'">' +
+              (t == '*' ? gettext('All Files') : t) + "</option>";
+            have_all_types = (have_all_types || (t == '*'));
+          }
+          i++;
+        }
+
+        if (!have_all_types) {
+          select_box += '<option value="*">' + gettext('All Files') + '</option>';
+        }
+        select_box += "</select><label>" + gettext('Format') + ": </label></div>";
+      }
+
+      $(".allowed_file_types").html(select_box);
+
+      $(".allowed_file_types select").on('change', function() {
+        var selected_val = $(this).val(),
+            curr_path = $('.currentpath').val();
+        getFolderInfo(curr_path, selected_val);
+      });
+    }
+
+    /*---------------------------------------------------------
+      Item Actions - Object events
+    ---------------------------------------------------------*/
+
+    // switch to folder view
+    $('.file_manager .fileinfo').on('click', function(e) {
+      $('.file_manager #uploader .input-path').val($('.currentpath').val())
+      enable_disable_btn();
+    });
+
+    // refresh current directory
+    $('.file_manager .refresh').on('click', function(e) {
+      enable_disable_btn();
+      var curr_path = $('.currentpath').val();
+        $('.file_manager #uploader .input-path').val(curr_path);
+        if(curr_path.endsWith("/")) {
+            var path = curr_path.substring(0, curr_path.lastIndexOf("/")) + "/";
+        } else {
+            var path = curr_path.substring(0, curr_path.lastIndexOf("\\")) + "\\";
+        }
+      getFolderInfo(path);
+    });
+
+    // hide message prompt and dimmer if clicked no
+    $('.delete_item button.btn_no').on('click', function() {
+      $('.delete_item, .fileinfo .fm_dimmer').hide();
+    });
+
+    // Disable home button on load
+    $('.file_manager').find('button.home').attr('disabled', 'disabled');
+    $('.file_manager').find('button.rename').attr('disabled', 'disabled');
+
+    // stop click event on dimmer click
+    $('.fileinfo .fm_dimmer').on('click', function(e) {
+      e.stopPropagation();
+    });
+
+    $('.fileinfo .replace_file').not(
+      $(this).find('span.pull-right')
+    ).on(
+    'click', function(e) {
+      $('#uploader .filemanager-btn-group').unbind().on(
+        'click', function() {
+          $('.fileinfo .delete_item, .fileinfo .replace_file, .fileinfo .fm_dimmer').hide();
+        });
+      e.stopPropagation();
+    });
+
+    // Set initial view state.
+    $('.fileinfo').data('view', config.options.defaultViewMode);
+    setViewButtonsFor(config.options.defaultViewMode);
+
+    // Upload click event
+    $('.file_manager .uploader').on('click', 'a', function(e) {
+      e.preventDefault();
+      var b = $('.currentpath').val();
+      var node_val = $(this).next().text();
+      parent = b.substring(0, b.slice(0, -1).lastIndexOf(node_val));
+      getFolderInfo(parent);
+    });
+
+    // re-render the home view
+    $('.file_manager .home').click(function() {
+      var currentViewMode = $('.fileinfo').data('view');
+      $('.fileinfo').data('view', currentViewMode);
+      getFolderInfo('/');
+      enab_dis_level_up();
+    });
+
+    // Go one directory back
+    $(".file_manager .level-up").click(function() {
+      var b = $('.currentpath').val();
+      // Enable/Disable level up button
+      enab_dis_level_up();
+
+      if (b.endsWith('\\') || b.endsWith('/')) {
+        b = b.substring(0, b.length - 1)
+      }
+
+      if (b != '/') {
+          if(b.lastIndexOf('/') > b.lastIndexOf('\\')) {
+            var parent = b.substring(0, b.slice(0, -1).lastIndexOf("/")) + "/";
+          } else {
+            var parent = b.substring(0, b.slice(0, -1).lastIndexOf("\\")) + "\\";
+          }
+
+          var d = $(".fileinfo").data("view");
+          $(".fileinfo").data("view", d);
+          getFolderInfo(parent);
+      }
+    });
+
+    // set buttons to switch between grid and list views.
+    $('.file_manager .grid').click(function() {
+      setViewButtonsFor('grid');
+      $('.fileinfo').data('view', 'grid');
+      enable_disable_btn();
+      getFolderInfo($('.currentpath').val());
+      save_file_dialog_view('grid', pgAdmin.FileUtils.transId);
+    });
+
+    // Show list mode
+    $('.file_manager .list').click(function() {
+      setViewButtonsFor('list');
+      $('.fileinfo').data('view', 'list');
+      enable_disable_btn();
+      getFolderInfo($('.currentpath').val());
+      save_file_dialog_view('list', pgAdmin.FileUtils.transId);
+    });
+
+    // Provide initial values for upload form, status, etc.
+    pgAdmin.FileUtils.setUploader(fileRoot);
+
+    var data;
+    this.data = data = {
+      'Capabilities': capabilities
+    };
+
+    function InputObject() {
+      this.init= function(cap) {
+        var self = this,
+            check_obj = function(path, check) {
+
+              var path = decodeURI(path);
+
+              if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+                if (
+                  has_capability(self.data_cap, 'select_folder')
+                ) {
+                  $('.file_manager_ok').removeClass('disabled');
+                  $('.file_manager button.delete, .file_manager button.rename').removeAttr(
+                    'disabled', 'disabled'
+                  );
+                  $('.file_manager button.download').attr(
+                    'disabled', 'disabled'
+                  );
+                  // set selected folder name in breadcrums
+                  $('.file_manager #uploader .input-path').hide();
+                  $('.file_manager #uploader .show_selected_file').remove();
+                  $('<span class="show_selected_file">'+path+'</span>').appendTo(
+                    '.file_manager #uploader .filemanager-path-group'
+                  );
+                } else {
+                  $('.file_manager_ok').addClass('disabled');
+                  if(check) {
+                    // Enable/Disable level up button
+                    enab_dis_level_up();
+
+                    $('.file_manager button.delete, .file_manager button.rename').attr('disabled', 'disabled');
+                    $('.file_manager button.download').attr('disabled', 'disabled');
+                    getFolderInfo(path);
+                  }
+                }
+              } else {
+                if (
+                  has_capability(self.data_cap, 'select_file')
+                ) {
+                  $('.file_manager_ok').removeClass('disabled');
+                  $('.file_manager button.delete, .file_manager button.download, .file_manager button.rename').removeAttr(
+                    'disabled'
+                  );
+                  // set selected folder name in breadcrums
+                  $('.file_manager #uploader .show_selected_file').remove();
+                }
+
+                if(check) {
+                  if (config.options.dialog_type == 'create_file') {
+                    var status = checkPermission(path)
+                    if (status) {
+                      $('.file_manager').trigger('enter-key');
+                    }
+                  } else if(config.options.dialog_type == 'select_file') {
+                    var file_status = getFileInfo(path);
+                    if (file_status) {
+                      $('.file_manager').trigger('enter-key');
+                    }
+                  }
+                }
+              }
+            };
+
+        self.data_cap = cap;
+
+        $('.storage_dialog #uploader .input-path').keyup(function(e) {
+          if(e.keyCode == 13) {
+            e.stopPropagation();
+            var path = $(this).val();
+            if(path == '') {
+              path = '/';
+            }
+
+            if(config.options.platform_type === "win32") {
+              path = path.replace(/\//g, '\\')
+            } else {
+              path = path.replace(/\\/g, '/')
+              if (!S.startsWith(path, '/')) {
+                path = '/' + path;
+              }
+            }
+
+            $(this).val(path);
+            setTimeout(function() {
+              check_obj(path, true);
+            });
+
+            return;
+          }
+          check_obj($(this).val(), false);
+        });
+      }
+      this.set_cap = function(cap) {
+        this.data_cap = cap;
+      }
+    }
+    var input_object = new InputObject();
+    input_object.init(data);
+
+    // Upload file
+    if (has_capability(data, 'upload')) {
+      Dropzone.autoDiscover = false;
+  // we remove simple file upload element
+  $('.file-input-container').remove();
+  $('.upload').remove();
+  $( ".create" ).before( '<button value="Upload" type="button" title="Upload File" name="upload" id="upload" class="btn fa fa-upload upload"><span></span></button> ' );
+
+  $('#uploader .upload').unbind().click(function() {
+    // we create prompt
+    var msg  = '<div id="dropzone-container">' +
+          '<button class="fa fa-times dz_cross_btn"></button>' +
+          '<div id="multiple-uploads" class="dropzone"></div>' +
+          '<div class="prompt-info">' + lg.file_size_limit +
+          config.upload.fileSizeLimit + ' ' + lg.mb + '.</div>',
+          error_flag = false,
+          path = $('.currentpath').val(),
+          filesizelimit = config.upload.fileSizeLimit,
+          // default dropzone value
+          fileSize = (filesizelimit != 'auto') ? filesizelimit : 256,
+          acceptFiles;
+
+    if (config.security.uploadPolicy == 'DISALLOW_ALL') {
+      acceptFiles = '.' + config.security.uploadRestrictions.join(',.');
+    } else {
+      // We allow any extension since we have no easy way to handle the the
+      // built-in `acceptedFiles` params would be handled later by the
+      // connector.
+      acceptFiles = null;
+    }
+
+    $('.file_manager .upload_file').toggle();
+    $('.file_manager .upload_file').html(msg);
+
+    //var previewTemplate = '<div id="dropzone-container">';
+    var previewTemplate = '<div class="file_upload_main dz-preview dz-file-preview">'+
+          '<div class="show_error">' +
+          '<p class="size dz-size" data-dz-size></p>' +
+          '<p class="name dz-filename" data-dz-name></p>' +
+          '</div>' +
+          '<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>' +
+          '<div class="dz-success-mark"><span></span></div>' +
+          '<div class="dz-error-mark"><span></span></div>' +
+          '<div class="dz-error-message"><span data-dz-errormessage></span></div>' +
+          '<a href="javascript:void(0);" class="fa fa-trash dz_file_remove" data-dz-remove></a>' +
+          '</div>';
+
+    $("div#multiple-uploads").dropzone({
+      paramName: "newfile",
+      url: pgAdmin.FileUtils.fileConnector,
+      maxFilesize: fileSize,
+      maxFiles: config.upload.number,
+      addRemoveLinks: true,
+      previewTemplate: previewTemplate,
+      parallelUploads: config.upload.number,
+      dictMaxFilesExceeded: lg.dz_dictMaxFilesExceeded.replace(
+        "%s", config.upload.number
+      ),
+      dictDefaultMessage: lg.dz_dictDefaultMessage,
+      dictInvalidFileType: lg.dz_dictInvalidFileType,
+      dictFileTooBig: lg.file_too_big + ' ' + lg.file_size_limit +
+        config.upload.fileSizeLimit + ' ' + lg.mb,
+      acceptedFiles: acceptFiles,
+      autoProcessQueue: true,
+      init: function() {
+        var dropzone = this;
+
+        $('.dz_cross_btn').unbind().on('click', function() {
+          $('.file_manager .upload_file').toggle();
+        });
+      },
+      sending: function(file, xhr, formData) {
+        formData.append("mode", "add");
+        formData.append("currentpath", path);
+        $('.upload_file .dz_cross_btn').attr('disabled', 'disabled');
+        setTimeout(function() {}, 10000);
+      },
+      success: function(file, response) {
+        var data = response.data.result,
+            $this = $(file.previewTemplate);
+
+        if (data.Code == 1) {
+          setTimeout(function() {
+            $this.find(".dz-upload").addClass("success");
+          }, 1000);
+          $this.find(".dz-upload").css('width', "100%").html("100%");
+          var alertifyWrapper = new AlertifyWrapper();
+          alertifyWrapper.success(lg.upload_success);
+        } else {
+          $this.find(".dz-upload").addClass("error");
+          $this.find(".dz-upload").css('width', "0%").html("0%");
+          var alertifyWrapper = new AlertifyWrapper();
+          alertifyWrapper.error(data.Error);
+        }
+        getFolderInfo(path);
+      },
+      totaluploadprogress: function(progress) {},
+      complete: function(file) {
+        if (file.status == "error") {
+          var alertifyWrapper = new AlertifyWrapper();
+          alertifyWrapper.error(lg.upload_error);
+        }
+        $('.upload_file .dz_cross_btn').removeAttr('disabled');
+        getFolderInfo(path);
+      }
+    });
+  });
+}
+
+    this.getDetailView(fileRoot);
+  },
+   /*
+   * Sets the folder status, upload, and new folder functions
+   * to the path specified. Called on initial page load and
+   * whenever a new directory is selected.
+   */
+  setUploader: function(path) {
+    var config = this.config;
+    var lg = this.lg;
+    $('.storage_dialog #uploader').find('a').remove();
+    $('.storage_dialog #uploader').find('b').remove();
+
+    if(config.options.platform_type === "win32") {
+      path = path.replace(/\//g, '\\')
+    } else {
+      path = path.replace(/\\/g, '/')
+    }
+
+    path = decodeURI(path);
+    if (config.options.platform_type === "win32") {
+      if (config.options.show_volumes && path == '\\') {
+        $('.storage_dialog #uploader .input-path').val('');
+      } else {
+          $('.storage_dialog #uploader .input-path').val(path);
+      }
+    } else if (!config.options.platform_type === "win32" &&
+          (path == '' || !S.startsWith(path, '/'))) {
+      path = '/' + path;
+      $('.storage_dialog #uploader .input-path').val(path);
+    } else {
+      $('.storage_dialog #uploader .input-path').val(path);
+    }
+
+    if( path.lastIndexOf('\\') == -1 && path.lastIndexOf('/') == -1) {
+      $('.currentpath').val(path);
+    } else if(path.lastIndexOf('/') > path.lastIndexOf('\\')) {
+      $('.currentpath').val(path.substr(0, path.lastIndexOf('/') + 1));
+    } else {
+      $('.currentpath').val(path.substr(0, path.lastIndexOf('\\') + 1));
+    }
+
+    enab_dis_level_up();
+    if ($('.storage_dialog #uploader h1 span').length === 0) {
+      $('<span>'+lg.current_folder+'</span>').appendTo($('.storage_dialog #uploader h1'));
+    }
+
+    $('.storage_dialog #uploader .input-path').attr('title', path);
+    $('.storage_dialog #uploader .input-path').attr('data-path', path);
+
+    // create new folder
+    $('.create').unbind().click(function() {
+      var foldername =  lg.new_folder;
+      var $file_element,
+          $file_element_list,
+          folder_div;
+
+
+      $('.file_manager button.create').attr('disabled', 'disabled');
+      if ($('.fileinfo').data('view') == 'grid') {
+
+        // template for creating new folder
+        folder_div =
+          "<li class='cap_download cap_delete cap_select_file cap_select_folder cap_rename cap_create cap_upload'>" +
+          "<div class='clip'><span data-alt='' class='fa fa-folder-open fm_folder'></span></div>" +
+          "<p><input type='text' class='fm_file_rename'><span title=''>New_Folder</span></p>" +
+          "<span class='meta size'></span><span class='meta created'></span><span class='meta modified'></span></li>";
+
+        path = $('.currentpath').val();
+        $file_element =  $(folder_div);
+        $('.fileinfo #contents.grid').append($file_element);
+        $file_element.find('p span').toggle();
+        $file_element.find('p input').toggle().val(lg.new_folder).select();
+
+        // rename folder/file on pressing enter key
+        $('.file_manager').bind().on('keyup', function(e) {
+          if (e.keyCode == 13) {
+            e.stopPropagation();
+            $file_element.find('p input').trigger('blur');
+          }
+        });
+
+        // rename folder/file on blur
+        $file_element.find('p input').on('blur', function(e) {
+          $('.file_manager button.create').removeAttr('disabled');
+          var text_value = $file_element.find('p input').val();
+
+          path = $('.currentpath').val();
+
+          $file_element.find('p input').toggle();
+          $file_element.find('p span').toggle().html(text_value);
+          if (text_value === undefined) {
+            text_value = lg.new_folder;
+          }
+          getFolderName(text_value);
+          getFolderInfo(path);
+        });
+
+      } else if ($('.fileinfo').data('view') == 'list') {
+        // template to create new folder in table view
+        folder_div = $(
+          "<tr class='cap_download cap_delete cap_select_file cap_select_folder cap_rename cap_create cap_upload'>" +
+          "<td title='' class='fa fa-folder-open tbl_folder'>" +
+          "<p><input type='text' class='fm_file_rename'><span>" +
+          lg.new_folder + "</span></p>" +
+          "</td><td><span title=''></span></td>" +
+          "<td></td>" +
+          "</tr>"
+        );
+
+        $file_element_list =  $(folder_div);
+        $('.fileinfo #contents.list').prepend($file_element_list);
+        $file_element_list.find('p span').toggle();
+        $file_element_list.find('p input').toggle().val(lg.new_folder).select();
+
+        // rename folder/file on pressing enter key
+        $('.file_manager').bind().on('keyup', function(e) {
+          if (e.keyCode == 13) {
+            e.stopPropagation();
+            $file_element_list.find('p input').trigger('blur');
+          }
+        });
+
+        // rename folder/file on blur
+        $file_element_list.find('p input').on('blur', function(e) {
+          $('.file_manager button.create').removeAttr('disabled');
+          var text_value = $file_element_list.find('p input').val();
+          path = $('.currentpath').val();
+          $file_element_list.find('p input').toggle();
+          $file_element_list.find('p span').toggle().html(text_value);
+          if (text_value === undefined) {
+            text_value = lg.new_folder;
+          }
+          getFolderName(text_value);
+          getFolderInfo(path);
+        });
+      }
+
+      // create a new folder
+      var getFolderName = function(value) {
+        var fname = value;
+
+        if (fname != '') {
+          foldername = fname;
+          var d = new Date(); // to prevent IE cache issues
+          $.getJSON(pgAdmin.FileUtils.fileConnector + '?mode=addfolder&path=' + $('.currentpath').val() + '&name=' + foldername, function(resp) {
+            var result = resp.data.result;
+            if (result.Code === 1) {
+              alertify.success(lg.successful_added_folder);
+              getFolderInfo(result.Parent);
+            } else {
+              alertify.error(result.Error);
+            }
+          });
+        } else {
+          alertify.error(lg.no_foldername);
+        }
+      };
+
+    });
+  },
+  /* Decides whether to retrieve file or folder info based on
+   * the path provided.
+   */
+  getDetailView: function(path) {
+    if (path.lastIndexOf('/') == path.length - 1 || path.lastIndexOf('\\') == path.length - 1) {
+      var allowed_types = this.config.options.allowed_file_types;
+      var set_type = allowed_types[0];
+      if (allowed_types[0] == "*") {
+        set_type = allowed_types[1];
+      }
+      getFolderInfo(path, set_type);
+    }
+  },
+  // helpful in show/hide toolbar button for Windows
+  hideButtons: function() {
+      return (
+        this.config.options.platform_type == 'win32' &&
+        $('.currentpath').val() === ''
+      );
+  },
+
+};
+return pgAdmin.FileUtils;
+});
+//@ sourceURL=utility.js
diff --git a/web/pgadmin/misc/sql/__init__.py b/web/pgadmin/misc/sql/__init__.py
index 13c81604..dd3e1670 100644
--- a/web/pgadmin/misc/sql/__init__.py
+++ b/web/pgadmin/misc/sql/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module providing utility functions for the application."""
 
+import os
 from flask import url_for
 from pgadmin.utils import PgAdminModule
 
@@ -19,8 +20,9 @@ class SQLModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.browser.object_sql',
-            'path': url_for('sql.static', filename='js/sql'),
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/sql'),
+            'when': None,
+            'is_template': False
         }]
 
 
diff --git a/web/pgadmin/misc/static/explain/js/explain.js b/web/pgadmin/misc/static/explain/js/explain.js
new file mode 100644
index 00000000..76acb8bd
--- /dev/null
+++ b/web/pgadmin/misc/static/explain/js/explain.js
@@ -0,0 +1,692 @@
+define (
+  'pgadmin.misc.explain',
+  ['sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin', 'backbone', 'snapsvg'],
+  function(url_for, $, _, S, pgAdmin, Backbone, Snap) {
+
+pgAdmin = pgAdmin || window.pgAdmin || {};
+var pgExplain = pgAdmin.Explain;
+
+// Snap.svg plug-in to write multitext as image name
+Snap.plugin(function (Snap, Element, Paper, glob) {
+  Paper.prototype.multitext = function (x, y, txt, max_width, attributes) {
+    var svg = Snap(),
+        abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+        isWordBroken = false,
+        temp = svg.text(0, 0, abc);
+
+    temp.attr(attributes);
+
+    /*
+     * Find letter width in pixels and
+     * index from where the text should be broken
+     */
+    var letter_width = temp.getBBox().width / abc.length,
+        word_break_index = Math.round((max_width / letter_width)) - 1;
+
+    svg.remove();
+
+    var words = txt.split(" "),
+        width_so_far = 0,
+        lines=[], curr_line = '',
+        /*
+         * Function to divide string into multiple lines
+         * and store them in an array if it size crosses
+         * the max-width boundary.
+         */
+        splitTextInMultiLine = function(leading, so_far, line) {
+          var l = line.length,
+              res = [];
+
+          if (l == 0)
+            return res;
+
+          if (so_far && (so_far + (l * letter_width) > max_width)) {
+            res.push(leading);
+            res = res.concat(splitTextInMultiLine('', 0, line));
+          } else if (so_far) {
+            res.push(leading + ' ' + line);
+          } else {
+            if (leading)
+                res.push(leading);
+            if (line.length > word_break_index + 1)
+                res.push(line.slice(0, word_break_index) + '-');
+            else
+                res.push(line);
+            res = res.concat(splitTextInMultiLine('', 0, line.slice(word_break_index)));
+          }
+
+          return res;
+        };
+
+    for (var i = 0; i < words.length; i++) {
+      var tmpArr = splitTextInMultiLine(
+            curr_line, width_so_far, words[i]
+          );
+
+      if (curr_line) {
+        lines = lines.slice(0, lines.length - 2);
+      }
+      lines = lines.concat(tmpArr);
+      curr_line = lines[lines.length - 1];
+      width_so_far = (curr_line.length * letter_width);
+    }
+
+    // Create multiple tspan for each string in array
+    var t = this.text(x,y,lines).attr(attributes);
+    t.selectAll("tspan:nth-child(n+2)").attr({
+      dy: "1.2em",
+      x: x
+    });
+    return t;
+  };
+});
+
+if (pgAdmin.Explain)
+    return pgAdmin.Explain;
+
+var pgExplain = pgAdmin.Explain = {
+   // Prefix path where images are stored
+   prefix: url_for('misc.index') + 'static/explain/img/'
+};
+
+/*
+ * A map which is used to fetch the image to be drawn and
+ * text which will appear below it
+ */
+var imageMapper = {
+    "Aggregate" : {
+        "image":"ex_aggregate.png", "image_text":"Aggregate"
+    },
+    'Append' : {
+        "image":"ex_append.png","image_text":"Append"
+    },
+    "Bitmap Index Scan" : function(data) {
+        return {
+            "image":"ex_bmp_index.png", "image_text":data['Index Name']
+        };
+    },
+    "Bitmap Heap Scan" : function(data) {
+  return {"image":"ex_bmp_heap.png","image_text":data['Relation Name']};
+},
+"BitmapAnd" : {"image":"ex_bmp_and.png","image_text":"Bitmap AND"},
+"BitmapOr" : {"image":"ex_bmp_or.png","image_text":"Bitmap OR"},
+"CTE Scan" : {"image":"ex_cte_scan.png","image_text":"CTE Scan"},
+"Function Scan" : {"image":"ex_result.png","image_text":"Function Scan"},
+"Foreign Scan" : {"image":"ex_foreign_scan.png","image_text":"Foreign Scan"},
+"Gather" : {"image":"ex_gather_motion.png","image_text":"Gather"},
+"Group" : {"image":"ex_group.png","image_text":"Group"},
+"GroupAggregate": {"image":"ex_aggregate.png","image_text":"Group Aggregate"},
+"Hash" : {"image":"ex_hash.png","image_text":"Hash"},
+"Hash Join": function(data) {
+  if (!data['Join Type']) return {"image":"ex_join.png","image_text":"Join"};
+  switch(data['Join Type']) {
+    case 'Anti': return {"image":"ex_hash_anti_join.png","image_text":"Hash Anti Join"};
+    case 'Semi': return {"image":"ex_hash_semi_join.png","image_text":"Hash Semi Join"};
+    default: return {"image":"ex_hash.png","image_text":String("Hash " + data['Join Type'] + " Join" )};
+  }
+},
+"HashAggregate" : {"image":"ex_aggregate.png","image_text":"Hash Aggregate"},
+"Index Only Scan" : function(data) {
+  return {"image":"ex_index_only_scan.png","image_text":data['Index Name']};
+},
+"Index Scan" : function(data) {
+  return {"image":"ex_index_scan.png","image_text":data['Index Name']};
+},
+"Index Scan Backword" : {"image":"ex_index_scan.png","image_text":"Index Backward Scan"},
+"Limit" : {"image":"ex_limit.png","image_text":"Limit"},
+"LockRows" : {"image":"ex_lock_rows.png","image_text":"Lock Rows"},
+"Materialize" : {"image":"ex_materialize.png","image_text":"Materialize"},
+"Merge Append": {"image":"ex_merge_append.png","image_text":"Merge Append"},
+"Merge Join": function(data) {
+  switch(data['Join Type']) {
+    case 'Anti': return {"image":"ex_merge_anti_join.png","image_text":"Merge Anti Join"};
+    case 'Semi': return {"image":"ex_merge_semi_join.png","image_text":"Merge Semi Join"};
+    default: return {"image":"ex_merge.png","image_text":String("Merge " + data['Join Type'] + " Join" )};
+  }
+},
+"ModifyTable" : function(data) {
+  switch (data['Operation']) {
+    case "Insert": return { "image":"ex_insert.png",
+                            "image_text":"Insert"
+                           };
+    case "Update": return {"image":"ex_update.png","image_text":"Update"};
+    case "Delete": return {"image":"ex_delete.png","image_text":"Delete"};
+  }
+},
+'Nested Loop' : function(data) {
+  switch(data['Join Type']) {
+    case 'Anti': return {"image":"ex_nested_loop_anti_join.png","image_text":"Nested Loop Anti Join"};
+    case 'Semi': return {"image":"ex_nested_loop_semi_join.png","image_text":"Nested Loop Semi Join"};
+    default: return {"image":"ex_nested.png","image_text":"Nested Loop " + data['Join Type'] + " Join"};
+  }
+},
+"Recursive Union" : {"image":"ex_recursive_union.png","image_text":"Recursive Union"},
+"Result" : {"image":"ex_result.png","image_text":"Result"},
+"Sample Scan" : {"image":"ex_scan.png","image_text":"Sample Scan"},
+"Scan" : {"image":"ex_scan.png","image_text":"Scan"},
+"Seek" : {"image":"ex_seek.png","image_text":"Seek"},
+"SetOp" : function(data) {
+  var strategy = data['Strategy'],
+      command = data['Command'];
+
+  if(strategy == "Hashed") {
+    if(S.startsWith(command, "Intersect")) {
+      if(command == "Intersect All")
+        return {"image":"ex_hash_setop_intersect_all.png","image_text":"Hashed Intersect All"};
+      return {"image":"ex_hash_setop_intersect.png","image_text":"Hashed Intersect"};
+    }
+    else if (S.startsWith(command, "Except")) {
+      if(command == "Except All")
+        return {"image":"ex_hash_setop_except_all.png","image_text":"Hashed Except All"};
+      return {"image":"ex_hash_setop_except.png","image_text":"Hash Except"};
+    }
+    return {"image":"ex_hash_setop_unknown.png","image_text":"Hashed SetOp Unknown"};
+  }
+  return {"image":"ex_setop.png","image_text":"SetOp"};
+},
+"Seq Scan": function(data) {
+  return {"image":"ex_scan.png","image_text":data['Relation Name']};
+},
+"Subquery Scan" : {"image":"ex_subplan.png","image_text":"SubQuery Scan"},
+"Sort" : {"image":"ex_sort.png","image_text":"Sort"},
+"Tid Scan" : {"image":"ex_tid_scan.png","image_text":"Tid Scan"},
+"Unique" : {"image":"ex_unique.png","image_text":"Unique"},
+"Values Scan" : {"image":"ex_values_scan.png","image_text":"Values Scan"},
+"WindowAgg" : {"image":"ex_window_aggregate.png","image_text":"Window Aggregate"},
+"WorkTable Scan" : {"image":"ex_worktable_scan.png","image_text":"WorkTable Scan"},
+"Undefined" : {"image":"ex_unknown.png","image_text":"Undefined"},
+}
+
+// Some predefined constants used to calculate image location and its border
+var pWIDTH =100.;
+var pHEIGHT = 100.;
+var IMAGE_WIDTH =50;
+var IMAGE_HEIGHT = 50;
+var offsetX = 200,
+    offsetY = 60;
+var ARROW_WIDTH = 10,
+    ARROW_HEIGHT = 10,
+    DEFAULT_ARROW_SIZE = 2;
+var TXT_ALLIGN = 5,
+    TXT_SIZE = "15px";
+var TOTAL_WIDTH = undefined,
+    TOTAL_HEIGHT = undefined;
+var xMargin = 25,
+    yMargin = 25;
+var MIN_ZOOM_FACTOR = 0.01,
+    MAX_ZOOM_FACTOR = 2,
+    INIT_ZOOM_FACTOR = 1;
+var ZOOM_RATIO = 0.05;
+
+
+// Backbone model for each plan property of input JSON object
+var PlanModel = Backbone.Model.extend({
+    defaults: {
+        "Plans": [],
+        level: [],
+        "image": undefined,
+        "image_text": undefined,
+        xpos: undefined,
+        ypos: undefined,
+        width: pWIDTH,
+        height: pHEIGHT
+    },
+    parse: function(data) {
+        var idx = 1,
+            lvl = data.level = data.level || [idx],
+            plans = [],
+            node_type = data['Node Type'],
+            // Calculating relative xpos of current node from top node
+            xpos = data.xpos = data.xpos - pWIDTH,
+            // Calculating relative ypos of current node from top node
+            ypos = data.ypos,
+            maxChildWidth = 0;
+
+        data['width'] = pWIDTH;
+        data['height'] = pHEIGHT;
+
+        /*
+         * calculating xpos, ypos, width and height if current node is a subplan
+         */
+        if (data['Parent Relationship'] === "SubPlan") {
+            data['width'] += (xMargin * 2) + (xMargin / 2);
+            data['height'] += (yMargin * 2);
+            data['ypos'] += yMargin;
+            xpos -= xMargin;
+            ypos += yMargin;
+        }
+
+        if(S.startsWith(node_type, "(slice"))
+            node_type = node_type.substring(0,7);
+
+        // Get the image information for current node
+        var mapperObj = (_.isFunction(imageMapper[node_type]) &&
+                imageMapper[node_type].apply(undefined, [data])) ||
+                imageMapper[node_type] || 'Undefined';
+
+        data["image"] = mapperObj["image"];
+        data["image_text"] = mapperObj["image_text"];
+
+        // Start calculating xpos, ypos, width and height for child plans if any
+        if ('Plans' in data) {
+
+            data['width'] += offsetX;
+
+            _.each(data['Plans'], function(p) {
+                var level = _.clone(lvl),
+                    plan = new PlanModel();
+
+                level.push(idx);
+                plan.set(plan.parse(_.extend(
+                    p, {
+                        "level": level,
+                        xpos: xpos - offsetX,
+                        ypos: ypos
+                    })));
+
+                if (maxChildWidth < plan.get('width')) {
+                    maxChildWidth = plan.get('width');
+                }
+
+                var childHeight = plan.get('height');
+
+                if (idx !== 1) {
+                    data['height'] = data['height'] + childHeight + offsetY;
+                } else if (childHeight > data['height']) {
+                    data['height'] = childHeight;
+                }
+                ypos += childHeight + offsetY;
+
+                plans.push(plan);
+                idx++;
+            });
+        }
+
+        // Final Width and Height of current node
+        data['width'] += maxChildWidth;
+        data['Plans'] = plans;
+
+        return data;
+    },
+
+    /*
+     * Required to parse and include non-default params of
+     * plan into backbone model
+     */
+    toJSON: function(non_recursive) {
+      var res = Backbone.Model.prototype.toJSON.apply(this, arguments);
+
+      if (non_recursive) {
+            delete res['Plans'];
+      } else {
+            var plans = [];
+            _.each(res['Plans'], function(p) {
+              plans.push(p.toJSON());
+            });
+            res['Plans'] = plans;
+      }
+      return res;
+    },
+
+    // Draw an arrow to parent node
+    drawPolyLine: function(g, startX, startY, endX, endY, opts, arrowOpts) {
+      // Calculate end point of first starting straight line (startx1, starty1)
+      // Calculate start point of 2nd straight line (endx1, endy1)
+      var midX1 = startX + ((endX - startX) / 3),
+          midX2 = startX + (2 * ((endX - startX) / 3));
+
+      //create arrow head
+      var arrow = g.polygon(
+                    [0, ARROW_HEIGHT,
+                    (ARROW_WIDTH / 2),ARROW_HEIGHT,
+                    (ARROW_HEIGHT / 4), 0,
+                    0, ARROW_WIDTH]
+                    ).transform("r90");
+      var marker = arrow.marker(
+                         0, 0, ARROW_WIDTH, ARROW_HEIGHT, 0, (ARROW_WIDTH / 2)
+                         ).attr(arrowOpts);
+
+      // First straight line
+      g.line(
+        startX, startY, midX1, startY
+        ).attr(opts);
+
+      // Diagonal line
+      g.line(
+        midX1-1, startY, midX2, endY
+        ).attr(opts);
+
+      // Last straight line
+      var line = g.line(
+                   midX2, endY, endX, endY
+                   ).attr(opts);
+      line.attr({markerEnd: marker})
+    },
+
+    // Draw image, its name and its tooltip
+    draw: function(s, xpos, ypos, pXpos, pYpos, graphContainer, toolTipContainer) {
+        var g = s.g();
+        var currentXpos = xpos + this.get('xpos') ,
+            currentYpos = ypos + this.get('ypos'),
+            isSubPlan = (this.get('Parent Relationship') === "SubPlan");
+
+        // Draw the subplan rectangle
+        if (isSubPlan) {
+          g.rect(
+            currentXpos - this.get('width') + pWIDTH + xMargin,
+            currentYpos - yMargin,
+            this.get('width') - xMargin,
+            this.get('height'), 5
+          ).attr({
+              stroke: '#444444',
+              'strokeWidth': 1.2,
+              fill: 'gray',
+              fillOpacity: 0.2
+          });
+
+          //provide subplan name
+          var text = g.text(
+            currentXpos  + pWIDTH - ( this.get('width') / 2) - xMargin,
+            currentYpos + pHEIGHT  - (this.get('height') / 2) - yMargin,
+            this.get('Subplan Name')
+          ).attr({
+            fontSize: TXT_SIZE, "text-anchor":"start",
+            fill: 'red'
+          });
+        }
+
+        // Draw the actual image for current node
+        var image = g.image(
+            pgExplain.prefix + this.get('image'),
+            currentXpos + (pWIDTH - IMAGE_WIDTH) / 2,
+            currentYpos + (pHEIGHT - IMAGE_HEIGHT) / 2,
+            IMAGE_WIDTH,
+            IMAGE_HEIGHT
+        );
+
+        // Draw tooltip
+        var image_data = this.toJSON();
+        image.mouseover(function(evt){
+
+          // Empty the tooltip content if it has any and add new data
+          toolTipContainer.empty();
+          var tooltip = $('<table></table>',{
+                           class: "pgadmin-tooltip-table"
+                        }).appendTo(toolTipContainer);
+          _.each(image_data, function(value,key) {
+            if(key !== 'image' && key !== 'Plans' &&
+               key !== 'level' && key !== 'image' &&
+               key !== 'image_text' && key !== 'xpos' &&
+               key !== 'ypos' && key !== 'width' &&
+               key !== 'height') {
+              tooltip.append( '<tr><td class="label explain-tooltip">' + key + '</td><td class="label explain-tooltip-val">' + value + '</td></tr>' );
+            };
+          });
+
+          var zoomFactor = graphContainer.data('zoom-factor');
+
+          // Calculate co-ordinates for tooltip
+          var toolTipX = ((currentXpos + pWIDTH) * zoomFactor - graphContainer.scrollLeft());
+          var toolTipY = ((currentYpos + pHEIGHT) * zoomFactor - graphContainer.scrollTop());
+
+          // Recalculate x.y if tooltip is going out of screen
+          if(graphContainer.width() < (toolTipX + toolTipContainer[0].clientWidth))
+            toolTipX -= (toolTipContainer[0].clientWidth + (pWIDTH*zoomFactor));
+          //if(document.children[0].clientHeight < (toolTipY + toolTipContainer[0].clientHeight))
+          if(graphContainer.height() < (toolTipY + toolTipContainer[0].clientHeight))
+            toolTipY -= (toolTipContainer[0].clientHeight + ((pHEIGHT/2)*zoomFactor));
+
+          toolTipX = toolTipX < 0 ? 0 : (toolTipX);
+          toolTipY = toolTipY < 0 ? 0 : (toolTipY);
+
+          // Show toolTip at respective x,y coordinates
+          toolTipContainer.css({'opacity': '0.8'});
+          toolTipContainer.css('left', toolTipX);
+          toolTipContainer.css( 'top', toolTipY);
+        });
+
+        // Remove tooltip when mouse is out from node's area
+        image.mouseout(function() {
+          toolTipContainer.empty();
+          toolTipContainer.css({'opacity': '0'});
+          toolTipContainer.css('left', 0);
+          toolTipContainer.css( 'top', 0);
+        });
+
+        // Draw text below the node
+        var node_label = (this.get('Schema') == undefined ?
+                            this.get('image_text') :
+                            this.get('Schema')+"."+this.get('image_text'));
+        var label = g.g();
+        g.multitext(
+          currentXpos + (pWIDTH / 2),
+          currentYpos + pHEIGHT - TXT_ALLIGN,
+          node_label,
+          150,
+          {"font-size": TXT_SIZE ,"text-anchor":"middle"}
+        );
+
+        // Draw Arrow to parent only its not the first node
+        if (!_.isUndefined(pYpos)) {
+            var startx = currentXpos + pWIDTH;
+            var starty = currentYpos + (pHEIGHT / 2);
+            var endx = pXpos - ARROW_WIDTH;
+            var endy = pYpos + (pHEIGHT / 2);
+            var start_cost = this.get("Startup Cost"),
+                total_cost = this.get("Total Cost");
+            var arrow_size = DEFAULT_ARROW_SIZE;
+            // Calculate arrow width according to cost of a particular plan
+            if(start_cost != undefined && total_cost != undefined) {
+              var arrow_size = Math.round(Math.log((start_cost+total_cost)/2 + start_cost));
+              arrow_size = arrow_size < 1 ? 1 : arrow_size > 10 ? 10 : arrow_size;
+            }
+
+
+            var arrow_view_box = [0, 0, 2*ARROW_WIDTH, 2*ARROW_HEIGHT];
+            var opts = {stroke: "#000000", strokeWidth: arrow_size + 1},
+                subplanOpts = {stroke: "#866486", strokeWidth: arrow_size + 1},
+                arrowOpts = {viewBox: arrow_view_box.join(" ")};
+
+            // Draw an arrow from current node to its parent
+            this.drawPolyLine(
+              g, startx, starty, endx, endy,
+              isSubPlan ? subplanOpts : opts, arrowOpts
+            );
+        }
+
+        var plans = this.get('Plans');
+
+        // Draw nodes for current plan's children
+        _.each(plans, function(p) {
+            p.draw(s, xpos, ypos, currentXpos, currentYpos, graphContainer, toolTipContainer);
+        });
+    }
+});
+
+// Main backbone model to store JSON object
+var MainPlanModel = Backbone.Model.extend({
+    defaults: {
+        "Plan": undefined,
+        xpos: 0,
+        ypos: 0,
+    },
+    initialize: function() {
+        this.set("Plan", new PlanModel());
+    },
+
+    // Parse the JSON data and fetch its children plans
+    parse: function(data) {
+        if (data && 'Plan' in data) {
+           var plan = this.get("Plan");
+           plan.set(
+             plan.parse(
+               _.extend(
+                 data['Plan'], {
+                   xpos: 0,
+                   ypos: 0
+                 })));
+
+           data['xpos'] = 0;
+           data['ypos'] = 0;
+           data['width'] = plan.get('width') + (xMargin * 2);
+           data['height'] = plan.get('height') + (yMargin * 2);
+
+           delete data['Plan'];
+        }
+
+      return data;
+    },
+    toJSON: function() {
+      var res = Backbone.Model.prototype.toJSON.apply(this, arguments);
+
+      if (res.Plan) {
+        res.Plan = res.Plan.toJSON();
+      }
+
+      return res;
+    },
+    draw: function(s, xpos, ypos, graphContainer, toolTipContainer) {
+        var g = s.g();
+
+        //draw the border
+        g.rect(
+	        0, 0, this.get('width') - 10, this.get('height') - 10, 5
+	    ).attr({
+            fill: '#FFF'
+        });
+
+        //Fetch total width, height
+        TOTAL_WIDTH = this.get('width');
+        TOTAL_HEIGHT = this.get('height');
+        var plan = this.get('Plan');
+
+        //Draw explain graph
+        plan.draw(g, xpos, ypos, undefined, undefined, graphContainer, toolTipContainer);
+    }
+});
+
+// Parse and draw full graphical explain
+_.extend(
+    pgExplain, {
+        // Assumption container is a jQuery object
+        DrawJSONPlan: function(container, plan) {
+          var my_plans = [];
+          container.empty();
+          var curr_zoom_factor = 1.0;
+
+          var zoomArea =$('<div></div>', {
+                class: 'pg-explain-zoom-area btn-group',
+                role: 'group'
+                }).appendTo(container),
+              zoomInBtn = $('<button></button>', {
+                class: 'btn pg-explain-zoom-btn badge',
+                title: 'Zoom in'
+                }).appendTo(zoomArea).append(
+                  $('<i></i>',{
+                    class: 'fa fa-search-plus'
+                  })),
+              zoomToNormal = $('<button></button>', {
+                class: 'btn pg-explain-zoom-btn badge',
+                title: 'Zoom to original'
+                }).appendTo(zoomArea).append(
+                  $('<i></i>',{
+                    class: 'fa fa-arrows-alt'
+                  })),
+              zoomOutBtn = $('<button></button>', {
+                class: 'btn pg-explain-zoom-btn badge',
+                title: 'Zoom out'
+                }).appendTo(zoomArea).append(
+                  $('<i></i>', {
+                    class: 'fa fa-search-minus'
+                  }));
+
+          // Main div to be drawn all images on
+          var planDiv = $('<div></div>',
+                           {class: "pgadmin-explain-container"}
+                         ).appendTo(container),
+              // Div to draw tool-tip on
+              toolTip = $('<div></div>',
+                           {id: "toolTip",
+                           class: "pgadmin-explain-tooltip"
+                           }
+                         ).appendTo(container);
+          toolTip.empty();
+          planDiv.data('zoom-factor', curr_zoom_factor);
+
+          var w = 0, h = 0,
+              x = xMargin, h = yMargin;
+
+          _.each(plan, function(p) {
+            var main_plan = new MainPlanModel();
+
+            // Parse JSON data to backbone model
+            main_plan.set(main_plan.parse(p));
+            w = main_plan.get('width');
+            h = main_plan.get('height');
+
+            var s = Snap(w, h),
+                $svg = $(s.node).detach();
+            planDiv.append($svg);
+            main_plan.draw(s, w - xMargin, yMargin, planDiv, toolTip);
+
+            var initPanelWidth = planDiv.width(),
+                initPanelHeight = planDiv.height();
+
+             /*
+              * Scale graph in case its width is bigger than panel width
+              * in which the graph is displayed
+              */
+            if(initPanelWidth < w) {
+              var width_ratio = initPanelWidth / w;
+
+              curr_zoom_factor = width_ratio;
+              curr_zoom_factor = curr_zoom_factor < MIN_ZOOM_FACTOR ? MIN_ZOOM_FACTOR : curr_zoom_factor;
+              curr_zoom_factor = curr_zoom_factor > INIT_ZOOM_FACTOR ? INIT_ZOOM_FACTOR : curr_zoom_factor;
+
+              var zoomInMatrix = new Snap.matrix();
+              zoomInMatrix.scale(curr_zoom_factor, curr_zoom_factor);
+
+              $svg.find('g').first().attr({transform: zoomInMatrix});
+              $svg.attr({'width': w * curr_zoom_factor, 'height': h * curr_zoom_factor});
+              planDiv.data('zoom-factor', curr_zoom_factor);
+            }
+
+            zoomInBtn.on('click', function(e){
+              curr_zoom_factor = ((curr_zoom_factor + ZOOM_RATIO) > MAX_ZOOM_FACTOR) ? MAX_ZOOM_FACTOR : (curr_zoom_factor + ZOOM_RATIO);
+              var zoomInMatrix = new Snap.matrix();
+              zoomInMatrix.scale(curr_zoom_factor, curr_zoom_factor);
+
+              $svg.find('g').first().attr({transform: zoomInMatrix});
+              $svg.attr({'width': w * curr_zoom_factor, 'height': h * curr_zoom_factor});
+              planDiv.data('zoom-factor', curr_zoom_factor);
+              zoomInBtn.blur();
+            });
+
+            zoomOutBtn.on('click', function(e) {
+              curr_zoom_factor = ((curr_zoom_factor - ZOOM_RATIO) < MIN_ZOOM_FACTOR) ? MIN_ZOOM_FACTOR : (curr_zoom_factor - ZOOM_RATIO);
+              var zoomInMatrix = new Snap.matrix();
+              zoomInMatrix.scale(curr_zoom_factor, curr_zoom_factor);
+
+              $svg.find('g').first().attr({transform: zoomInMatrix});
+              $svg.attr({'width': w * curr_zoom_factor, 'height': h * curr_zoom_factor});
+              planDiv.data('zoom-factor', curr_zoom_factor);
+              zoomOutBtn.blur();
+            });
+
+            zoomToNormal.on('click', function(e) {
+              curr_zoom_factor = INIT_ZOOM_FACTOR;
+              var zoomInMatrix = new Snap.matrix();
+              zoomInMatrix.scale(curr_zoom_factor, curr_zoom_factor);
+
+              $svg.find('g').first().attr({transform: zoomInMatrix});
+              $svg.attr({'width': w * curr_zoom_factor, 'height': h * curr_zoom_factor});
+              planDiv.data('zoom-factor', curr_zoom_factor);
+              zoomToNormal.blur();
+            });
+          });
+
+        }
+    });
+
+    return pgExplain;
+});
diff --git a/web/pgadmin/misc/statistics/__init__.py b/web/pgadmin/misc/statistics/__init__.py
index b1da4b67..028acfde 100644
--- a/web/pgadmin/misc/statistics/__init__.py
+++ b/web/pgadmin/misc/statistics/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module providing utility functions for the application."""
 
+import os
 from flask import url_for
 from pgadmin.utils import PgAdminModule
 
@@ -26,8 +27,9 @@ class StatisticsModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.browser.object_statistics',
-            'path': url_for('statistics.static', filename='js/statistics'),
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/statistics'),
+            'when': None,
+            'is_template': False
         }]
 
 
diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py
index e3af7d03..38de8aeb 100644
--- a/web/pgadmin/preferences/__init__.py
+++ b/web/pgadmin/preferences/__init__.py
@@ -12,6 +12,7 @@ Implements the routes for creating Preferences/Options Dialog on the client
 side and for getting/setting preferences.
 """
 
+import os
 import simplejson as json
 from flask import render_template, url_for, Response, request, session
 from flask_babel import gettext
@@ -36,8 +37,9 @@ class PreferencesModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.preferences',
-            'path': url_for('preferences.index') + 'preferences',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/preferences'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_own_stylesheets(self):
diff --git a/web/pgadmin/preferences/static/js/preferences.js b/web/pgadmin/preferences/static/js/preferences.js
new file mode 100644
index 00000000..c785fbb5
--- /dev/null
+++ b/web/pgadmin/preferences/static/js/preferences.js
@@ -0,0 +1,415 @@
+define('pgadmin.preferences', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'alertify',
+  'pgadmin', 'backform', 'pgadmin.browser', 'pgadmin.backform'
+], function(gettext, url_for, $, _, alertify, pgAdmin, Backform, pgBrowser) {
+  // This defines the Preference/Options Dialog for pgAdmin IV.
+    pgAdmin = pgAdmin || window.pgAdmin || {};
+
+    /*
+     * Hmm... this module is already been initialized, we can refer to the old
+     * object from here.
+     */
+    if (pgAdmin.Preferences)
+        return pgAdmin.Preferences;
+
+    pgAdmin.Preferences = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        // Declare the Preferences dialog
+        alertify.dialog('preferencesDlg', function() {
+
+          var jTree,         // Variable to create the aci-tree
+              controls = [], // Keep tracking of all the backform controls
+                             // created by the dialog.
+              // Dialog containter
+              $container = $("<div class='preferences_dialog'></div>");
+
+
+          /*
+           * Preference Model
+           *
+           * This model will be used to keep tracking of the changes done for
+           * an individual option.
+           */
+          var PreferenceModel = Backbone.Model.extend({
+            idAttribute: 'id',
+            defaults: {
+              id: undefined,
+              value: undefined
+            }
+          });
+
+          /*
+           * Preferences Collection object.
+           *
+           * We will use only one collection object to keep track of all the
+           * preferences.
+           */
+          var changed = {},
+              preferences = this.preferences = new (Backbone.Collection.extend({
+                model: PreferenceModel,
+                url: url_for('preferences.index'),
+                updateAll: function() {
+                  // We will send only the modified data to the server.
+                  for (var key in changed) {
+                    this.get(key).save();
+                  }
+                  return true;
+                }
+              }))(null);
+
+          preferences.on('reset', function() {
+            // Reset the changed variables
+            changed = {};
+          });
+
+          preferences.on('change', function(m) {
+            var id = m.get('id');
+            if (!(id in changed)) {
+              // Keep track of the original value
+              changed[id] = m._previousAttributes.value;
+            } else if (_.isEqual(m.get('value'), changed[id])) {
+              // Remove unchanged models.
+              delete changed[id];
+            }
+          });
+
+          /*
+           * Function: renderPreferencePanel
+           *
+           * Renders the preference panel in the content div based on the given
+           * preferences.
+           */
+          var renderPreferencePanel = function(prefs) {
+            /*
+             * Clear the existing html in the preferences content
+             */
+            var content = $container.find('.preferences_content');
+            content.empty();
+
+            /*
+             * We should clean up the existing controls.
+             */
+            if (controls) {
+              _.each(controls, function(c) {
+                c.remove();
+              });
+            }
+            controls = [];
+
+            /*
+             * We will create new set of controls and render it based on the
+             * list of preferences using the Backform Field, Control.
+             */
+            _.each(prefs, function(p) {
+
+              var m = preferences.get(p.id);
+              m.errorModel = new Backbone.Model();
+              var f = new Backform.Field(
+                    _.extend({}, p, {id: 'value', name: 'value'})
+                    ),
+                  cntr = new (f.get("control")) ({
+                    field: f,
+                    model: m
+                  });
+                  content.append(cntr.render().$el);
+
+                  // We will keep track of all the controls rendered at the
+                  // moment.
+                  controls.push(cntr);
+            });
+
+          };
+
+          /*
+           * Function: dialogContentCleanup
+           *
+           * Do the dialog container cleanup on openning.
+           */
+
+          var dialogContentCleanup = function() {
+              // Remove the existing preferences
+              if (!jTree)
+                return;
+
+              /*
+               * Remove the aci-tree (mainly to remove the jquery object of
+               * aciTree from the system for this container).
+               */
+              try {
+                jTreeApi = jTree.aciTree('destroy');
+              } catch(ex) {
+                // Sometimes - it fails to destroy the tree properly and throws
+                // exception.
+              }
+              jTree.off('acitree', treeEventHandler);
+
+              // We need to reset the data from the preferences too
+              preferences.reset();
+
+              /*
+               * Clean up the existing controls.
+               */
+              if (controls) {
+                _.each(controls, function(c) {
+                  c.remove();
+                });
+              }
+              controls = [];
+
+              // Remove all the objects now.
+              $container.empty();
+            },
+            /*
+             * Function: selectFirstCategory
+             *
+             * Whenever a user select a module instead of a category, we should
+             * select the first categroy of it.
+             */
+            selectFirstCategory = function(api, item) {
+              var  data = item ? api.itemData(item) : null;
+
+              if (data && data.preferences) {
+                  api.select(item);
+                  return;
+              }
+              item = api.first(item);
+              selectFirstCategory(api, item);
+            },
+            /*
+             * A map on how to create controls for each datatype in preferences
+             * dialog.
+             */
+            getControlMappedForType = function(p) {
+              switch(p.type) {
+                case 'text':
+                    return 'input';
+                case 'boolean':
+                  p.options = {
+                      onText: gettext('True'),
+                      offText: gettext('False'),
+                      onColor: 'success',
+                      offColor: 'default',
+                      size: 'mini'
+                    };
+                  return 'switch';
+                case 'node':
+                    p.options = {
+                      onText: gettext('Show'),
+                      offText: gettext('Hide'),
+                      onColor: 'success',
+                      offColor: 'default',
+                      size: 'mini'
+                    };
+                    return 'switch';
+                case 'integer':
+                  return 'numeric';
+                case 'numeric':
+                  return 'numeric';
+                case 'date':
+                  return 'datepicker';
+                case 'datetime':
+                  return 'datetimepicker';
+                case 'options':
+                  var opts = [];
+                  // Convert the array to SelectControl understandable options.
+                  _.each(p.options, function(o) {
+                    if('label' in o && 'value' in o){
+                      opts.push({'label': o.label, 'value': o.value});
+                    } else {
+                      opts.push({'label': o, 'value': o});
+                    }
+                  });
+                  p.options = opts;
+                  return 'select2';
+                case 'multiline':
+                  return 'textarea';
+                case 'switch':
+                  return 'switch';
+                default:
+                    if (console && console.log) {
+                      // Warning for developer only.
+                       console.log(
+                         "Hmm.. We don't know how to render this type - ''" + type + "' of control."
+                       );
+                    }
+                  return 'input';
+              }
+            },
+            /*
+             * function: treeEventHandler
+             *
+             * It is basically a callback, which listens to aci-tree events,
+             * and act accordingly.
+             *
+             * + Selection of the node will existance of the preferences for
+             *   the selected tree-node, if not pass on to select the first
+             *   category under a module, else pass on to the render function.
+             *
+             * + When a new node is added in the tree, it will add the relavent
+             *   preferences in the preferences model collection, which will be
+             *   called during initialization itself.
+             *
+             *
+             */
+            treeEventHandler = function(event, api, item, eventName) {
+              // Look for selected item (if none supplied)!
+              item = item || api.selected();
+
+              // Event tree item has itemData
+              var d = item ? api.itemData(item) : null;
+
+              /*
+               * boolean (switch/checkbox), string, enum (combobox - enumvals),
+               * integer (min-max), font, color
+               */
+              switch (eventName) {
+                case "selected":
+                  if (!d)
+                    return true;
+
+                  if (d.preferences) {
+                    /*
+                     * Clear the existing html in the preferences content
+                     */
+                    renderPreferencePanel(d.preferences);
+
+                    return true;
+                  } else{
+                    selectFirstCategory(api, item);
+                  }
+                  break;
+                case 'added':
+                  if (!d)
+                    return true;
+
+                  // We will add the preferences in to the preferences data
+                  // collection.
+                  if (d.preferences && _.isArray(d.preferences)) {
+                    _.each(d.preferences, function(p) {
+                      preferences.add({
+                        'id': p.id, 'value': p.value, 'cid': d.id, 'mid': d.mid
+                      });
+                      /*
+                       * We don't know until now, how to render the control for
+                       * this preference.
+                       */
+                      if (!p.control) {
+                        p.control = getControlMappedForType(p);
+                      }
+                      if (p.help_str) {
+                        p.helpMessage = p.help_str;
+                      }
+                    });
+                  }
+                  d.sortable = false;
+                  break;
+                case 'loaded':
+                  // Let's select the first category from the prefrences.
+                  // We need to wait for sometime before all item gets loaded
+                  // properly.
+                  setTimeout(
+                    function() {
+                      selectFirstCategory(api, null);
+                    }, 300);
+                  break;
+              }
+              return true;
+          };
+
+          // Dialog property
+          return {
+            main: function() {
+
+              // Remove the existing content first.
+              dialogContentCleanup();
+
+              $container.append(
+                "<div class='pg-el-xs-3 preferences_tree aciTree'></div>"
+              ).append(
+                "<div class='pg-el-xs-9 preferences_content'>" +
+                gettext('Category is not selected.') +
+                "</div>"
+              );
+
+              // Create the aci-tree for listing the modules and categories of
+              // it.
+              jTree = $container.find('.preferences_tree');
+              jTree.on('acitree', treeEventHandler);
+
+              jTree.aciTree({
+                selectable: true,
+                expand: true,
+                ajax: {
+                  url: url_for('preferences.index')
+                }
+              });
+
+              this.show();
+            },
+            setup: function() {
+              return {
+                buttons:[{
+                  text: '', key: 112,
+                  className: 'btn btn-default pull-left fa fa-lg fa-question',
+                  attrs:{
+                    name:'dialog_help', type:'button',
+                    label: gettext('Preferences'),
+                    url: url_for(
+                      'help.static', {'filename': 'preferences.html'}
+                    )
+                  }
+                },{
+                  text: gettext('OK'), key: 13, className: "btn btn-primary fa fa-lg fa-save pg-alertify-button"
+                },{
+                  text: gettext('Cancel'), key: 27, className: "btn btn-danger fa fa-lg fa-times pg-alertify-button"
+                }],
+                focus: { element: 0 },
+                options: {
+                  padding: !1,
+                  overflow: !1,
+                  title: gettext('Preferences'),
+                  closableByDimmer: false,
+                  modal:false,
+                  pinnable: false
+                }
+              };
+            },
+            callback: function(e) {
+              if (e.button.element.name == "dialog_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    null, null, e.button.element.getAttribute('label'));
+                  return;
+               }
+
+              if (e.button.text == gettext('OK')){
+                preferences.updateAll();
+                // Refresh preferences cache
+                pgBrowser.cache_preferences();
+              }
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+              alertify.pgDialogBuild.apply(this)
+            },
+            hooks: {
+              onshow: function() {
+                $(this.elements.body).addClass('pgadmin-preference-body');
+              }
+            }
+          };
+        });
+
+      },
+      show: function() {
+        alertify.preferencesDlg(true).resizeTo('60%', '60%');
+      }
+    };
+
+    return pgAdmin.Preferences;
+  });
diff --git a/web/pgadmin/settings/__init__.py b/web/pgadmin/settings/__init__.py
index 197ca4cb..578b6d28 100644
--- a/web/pgadmin/settings/__init__.py
+++ b/web/pgadmin/settings/__init__.py
@@ -9,6 +9,7 @@
 
 """Utility functions for storing and retrieving user configuration settings."""
 
+import os
 import traceback
 
 from flask import Response, request, render_template, url_for
@@ -28,8 +29,9 @@ class SettingsModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.settings',
-            'path': url_for('settings.index') + 'settings',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/settings'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_own_menuitems(self):
diff --git a/web/pgadmin/settings/static/js/settings.js b/web/pgadmin/settings/static/js/settings.js
new file mode 100644
index 00000000..989de7f8
--- /dev/null
+++ b/web/pgadmin/settings/static/js/settings.js
@@ -0,0 +1,59 @@
+define('pgadmin.settings',
+  [
+    'jquery', 'alertify', 'pgadmin', 'underscore', 'backform',
+    'sources/gettext', 'sources/url_for', 'pgadmin.backform'
+  ],
+  // This defines the Preference/Options Dialog for pgAdmin IV.
+  function($, alertify, pgAdmin, _, Backform, gettext, url_for) {
+    pgAdmin = pgAdmin || window.pgAdmin || {};
+
+    /*
+     * Hmm... this module is already been initialized, we can refer to the old
+     * object from here.
+     */
+    if (pgAdmin.Settings)
+        return pgAdmin.Settings;
+
+    pgAdmin.Settings = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+      },
+      // We will force unload method to not to save current layout
+      // and reload the window
+      show: function() {
+        var obj = this;
+        alertify.confirm(gettext('Reset layout'),
+          gettext('Are you sure you want to reset the current layout? This will cause the application to reload and any un-saved data will be lost.'),
+          function() {
+            var reloadingIndicator = $('<div id="reloading-indicator"></div>');
+            $('body').append(reloadingIndicator);
+            // Delete the record from database as well, then only reload page
+            $.ajax({
+              url: url_for('settings.reset_layout'),
+              type: 'DELETE',
+              async: false,
+              success: function() {
+                // Prevent saving layout on server for next page reload.
+                $(window).unbind('unload');
+                window.onbeforeunload = null;
+                // Now reload page
+                location.reload(true);
+              },
+              error: function() {
+                console.log('Something went wrong on server while resetting layout');
+              }
+            });
+
+          },
+          function() {
+            // Do nothing as user canceled the operation.
+          }
+        );
+      }
+    };
+
+    return pgAdmin.Settings;
+  });
diff --git a/web/pgadmin/static/bundle/app.js b/web/pgadmin/static/bundle/app.js
index e4d6c421..a8c9e07d 100644
--- a/web/pgadmin/static/bundle/app.js
+++ b/web/pgadmin/static/bundle/app.js
@@ -1,5 +1,5 @@
 define('app', [
-  'babel-polyfill','pgadmin', 'browser_node', 'tools.datagrid'
+  'babel-polyfill', 'pgadmin', 'bundled_browser', 'pgadmin.datagrid'
   ], function() {
     var initializeModules = function(Object) {
       for (var key in Object) {
@@ -20,4 +20,4 @@ define('app', [
 
     // create menus after all modules are initialized.
     pgAdmin.Browser.create_menus();
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/static/bundle/browser.js b/web/pgadmin/static/bundle/browser.js
index cf3a9089..c415d2e9 100644
--- a/web/pgadmin/static/bundle/browser.js
+++ b/web/pgadmin/static/bundle/browser.js
@@ -1,5 +1,5 @@
-define('browser_node',[
+define('bundled_browser',[
  'pgadmin.browser'
 ], function(pgBrowser) {
   pgBrowser.init();
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/static/bundle/codemirror.js b/web/pgadmin/static/bundle/codemirror.js
index c8766ff1..4c79d1de 100644
--- a/web/pgadmin/static/bundle/codemirror.js
+++ b/web/pgadmin/static/bundle/codemirror.js
@@ -14,6 +14,6 @@ import 'codemirror/addon/search/jump-to-line';
 import 'codemirror/addon/edit/matchbrackets';
 import 'codemirror/addon/edit/closebrackets';
 import 'codemirror/addon/comment/comment'
-import 'pgadmin.sqlfoldcode';
+import '../js/codemirror/addon/fold/pgadmin-sqlfoldcode';
 
 export default CodeMirror;
\ No newline at end of file
diff --git a/web/pgadmin/static/js/url_for.js b/web/pgadmin/static/js/url_for.js
index 700776db..b1005381 100644
--- a/web/pgadmin/static/js/url_for.js
+++ b/web/pgadmin/static/js/url_for.js
@@ -1,5 +1,4 @@
 define(["pgadmin.browser.endpoints"], function (endpoints) {
-
   /***
    * This method behaves as a drop-in replacement for flask url_for function.
    * It uses the exposed URLs file under the hood, and replace the substitions provided by the modules.
diff --git a/web/pgadmin/tools/__init__.py b/web/pgadmin/tools/__init__.py
index 167e257f..206d014f 100644
--- a/web/pgadmin/tools/__init__.py
+++ b/web/pgadmin/tools/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module container for keeping all submodule of type tool."""
 
+import os
 from flask import render_template, Response
 from flask import url_for
 from flask_babel import get_translations, gettext
@@ -23,28 +24,22 @@ class ToolsModule(PgAdminModule):
         return [{
             'name': 'translations',
             'path': url_for('tools.index') + "translations",
-            'when': None
-        },{
-            'name': 'pgadmin-sqlfoldcode',
-            'path': url_for(
-                'static',
-                filename='js/codemirror/addon/fold/pgadmin-sqlfoldcode'
-            ),
-            'when': 'debugger'
+            'when': None,
+            'is_template': True
         },{
             'name': 'slick.pgadmin.editors',
-            'path': url_for(
-                'static',
-                filename='js/slickgrid/slick.pgadmin.editors'
+            'path': os.path.join(
+                self.root_path, '../static/js/slickgrid/slick.pgadmin.editors'
             ),
-            'when': 'debugger'
+            'when': 'debugger',
+            'is_template': False
         },{
             'name': 'slick.pgadmin.formatters',
-            'path': url_for(
-                'static',
-                filename='js/slickgrid/slick.pgadmin.formatters'
+            'path': os.path.join(
+                self.root_path, '../static/js/slickgrid/slick.pgadmin.formatters'
             ),
-            'when': 'debugger'
+            'when': 'debugger',
+            'is_template': False
         }]
 
 # Initialise the module
@@ -64,4 +59,4 @@ def translations():
         response=template,
         status=200,
         mimetype="application/javascript"
-    )
\ No newline at end of file
+    )
diff --git a/web/pgadmin/tools/backup/__init__.py b/web/pgadmin/tools/backup/__init__.py
index cf561c3e..bb8ec0ed 100644
--- a/web/pgadmin/tools/backup/__init__.py
+++ b/web/pgadmin/tools/backup/__init__.py
@@ -48,8 +48,9 @@ class BackupModule(PgAdminModule):
         """
         return [{
             'name': 'pgadmin.tools.backup',
-            'path': url_for('backup.index') + 'backup',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/backup'),
+            'when': None,
+            'is_template': False
         }]
 
     def show_system_objects(self):
diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js
new file mode 100644
index 00000000..361926da
--- /dev/null
+++ b/web/pgadmin/tools/backup/static/js/backup.js
@@ -0,0 +1,812 @@
+// Backup dialog
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin.browser', 'backbone', 'backgrid', 'backform', 'pgadmin.browser.node',
+  'sources/alerts/alertify_wrapper'
+], function(gettext, url_for, $, _, S, alertify, pgBrowser, Backbone, Backgrid, Backform, pgNode, AlertifyWrapper) {
+
+  // if module is already initialized, refer to that.
+  if (pgBrowser.Backup) {
+    return pgBrowser.Backup;
+  }
+
+/*
+=====================
+TODO LIST FOR BACKUP:
+=====================
+1) Add Object tree on object tab which allows user to select
+   objects which can be backed up
+2) Allow user to select/deselect objects
+3) If database is selected in browser
+   show all database children objects selected in Object tree
+4) If schema is selected in browser
+   show all schema children objects selected in Object tree
+5) If table is selected then show table/schema/database selected
+   in Object tree
+6) if root objects like database/schema is not selected and their
+   children are selected then add them separately with in tables attribute
+   with schema.
+*/
+
+    var CustomSwitchControl = Backform.CustomSwitchControl = Backform.SwitchControl.extend({
+        template: _.template([
+          '<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
+          '<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
+          '  <div class="checkbox">',
+          '    <label>',
+          '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>"',
+          '        name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%>',
+          '        <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+          '    </label>',
+          '  </div>',
+          '</div>',
+          '<% if (helpMessage && helpMessage.length) { %>',
+          '  <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+          '<% } %>'
+        ].join("\n")),
+        className: 'pgadmin-control-group form-group pg-el-md-6 pg-el-xs-12'
+    });
+
+    //Backup Model (Server Node)
+    var BackupModel = Backbone.Model.extend({
+      idAttribute: 'id',
+      defaults: {
+        file: undefined,
+        role: undefined,
+        dqoute: false,
+        verbose: true,
+        type: undefined /* global, server */
+      },
+      schema: [{
+        id: 'file', label: gettext('Filename'),
+        type: 'text', disabled: false, control: Backform.FileControl,
+        dialog_type: 'create_file', supp_types: ['*', 'sql', 'backup']
+      },{
+        id: 'role', label: gettext('Role name'),
+        control: 'node-list-by-name', node: 'role',
+        select2: { allowClear: false }
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Miscellaneous'),
+        schema:[{
+          id: 'verbose', label: gettext('Verbose messages'),
+          control: Backform.CustomSwitchControl, disabled: false,
+          group: gettext('Miscellaneous')
+        },{
+          id: 'dqoute', label: gettext('Force double quote on identifiers'),
+          control: Backform.CustomSwitchControl, disabled: false,
+          group: gettext('Miscellaneous')
+        }]
+      },{
+        id: 'server_note', label: gettext('Note'),
+        text: gettext('The backup format will be PLAIN'),
+        type: 'note', visible: function(m){
+          return m.get('type') === 'server';
+        }
+      },{
+        id: 'globals_note', label: gettext('Note'),
+        text: gettext('Only objects global to the entire database will be backed up in PLAIN format'),
+        type: 'note', visible: function(m){
+          return m.get('type') === 'globals';
+        }
+     },{
+     }],
+      validate: function() {
+        // TODO: HOW TO VALIDATE ???
+        return null;
+      }
+    });
+
+    //Backup Model (Objects like Database/Schema/Table)
+    var BackupObjectModel = Backbone.Model.extend({
+      idAttribute: 'id',
+      defaults: {
+        file: undefined,
+        role: undefined,
+        format: 'custom',
+        verbose: true,
+        blobs: true,
+        encoding: undefined,
+        schemas: [],
+        tables: [],
+        database: undefined
+      },
+      schema: [{
+        id: 'file', label: gettext('Filename'),
+        type: 'text', disabled: false, control: Backform.FileControl,
+        dialog_type: 'create_file', supp_types: ['*', 'sql', 'backup']
+      },{
+        id: 'format', label: gettext('Format'),
+        type: 'text', disabled: false,
+        control: 'select2', select2: {
+            allowClear: false,
+            width: "100%"
+        },
+        options: [
+          {label: gettext('Custom'), value: "custom"},
+          {label: gettext('Tar'), value: "tar"},
+          {label: gettext('Plain'), value: "plain"},
+          {label: gettext('Directory'), value: "directory"}
+        ]
+      },{
+        id: 'ratio', label: gettext('Compression ratio'),
+        type: 'int', min: 0, max:9, disabled: false
+      },{
+        id: 'encoding', label: gettext('Encoding'),
+        type: 'text', disabled: false,  node: 'database',
+        control: 'node-ajax-options', url: 'get_encodings'
+      },{
+        id: 'no_of_jobs', label: gettext('Number of jobs'),
+        type: 'int', deps: ['format'], disabled: function(m) {
+          return !(m.get('format') === "Directory");
+        }
+      },{
+        id: 'role', label: gettext('Role name'),
+        control: 'node-list-by-name', node: 'role',
+        select2: { allowClear: false }
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Sections'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'pre_data', label: gettext('Pre-data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return m.get('only_data')
+                   || m.get('only_schema');
+          }
+        },{
+          id: 'data', label: gettext('Data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return m.get('only_data')
+                   || m.get('only_schema');
+          }
+        },{
+          id: 'post_data', label: gettext('Post-data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return m.get('only_data')
+                   || m.get('only_schema');
+          }
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Type of objects'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'only_data', label: gettext('Only data'),
+          control: Backform.CustomSwitchControl, group: gettext('Type of objects'),
+          deps: ['pre_data', 'data', 'post_data','only_schema'], disabled: function(m) {
+            return m.get('pre_data')
+                   || m.get('data')
+                   || m.get('post_data')
+                   || m.get('only_schema');
+          }
+        },{
+          id: 'only_schema', label: gettext('Only schema'),
+          control: Backform.CustomSwitchControl, group: gettext('Type of objects'),
+          deps: ['pre_data', 'data', 'post_data', 'only_data'], disabled: function(m) {
+            return m.get('pre_data')
+                   || m.get('data')
+                   || m.get('post_data')
+                   || m.get('only_data');
+          }
+        },{
+          id: 'blobs', label: gettext('Blobs'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Type of objects')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Do not save'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'dns_owner', label: gettext('Owner'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        },{
+          id: 'dns_privilege', label: gettext('Privilege'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        },{
+          id: 'dns_tablespace', label: gettext('Tablespace'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        },{
+          id: 'dns_unlogged_tbl_data', label: gettext('Unlogged table data'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Queries'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'use_column_inserts', label: gettext('Use Column Inserts'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        },{
+          id: 'use_insert_commands', label: gettext('Use Insert Commands'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        },{
+          id: 'include_create_database', label: gettext('Include CREATE DATABASE statement'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        },{
+          id: 'include_drop_database', label: gettext('Include DROP DATABASE statement'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Disable'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'disable_trigger', label: gettext('Trigger'),
+          control: Backform.CustomSwitchControl, group: gettext('Disable'),
+          deps: ['only_data'], disabled: function(m) {
+            return !(m.get('only_data'));
+          }
+        },{
+          id: 'disable_quoting', label: gettext('$ quoting'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Disable')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Miscellaneous'),
+        group: gettext('Dump options'),
+        schema:[{
+          id: 'with_oids', label: gettext('With OID(s)'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Miscellaneous')
+        },{
+          id: 'verbose', label: gettext('Verbose messages'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Miscellaneous')
+        },{
+          id: 'dqoute', label: gettext('Force double quote on identifiers'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Miscellaneous')
+        },{
+          id: 'use_set_session_auth', label: gettext('Use SET SESSION AUTHORIZATION'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Miscellaneous')
+        }]
+      }],
+      validate: function() {
+        return null;
+      }
+    });
+
+    // Create an Object Backup of pgBrowser class
+    pgBrowser.Backup  = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        // Define list of nodes on which backup context menu option appears
+        var backup_supported_nodes = [
+              'database', 'schema', 'table', 'partition'
+            ];
+
+        /**
+          Enable/disable backup menu in tools based
+          on node selected
+          if selected node is present in supported_nodes,
+          menu will be enabled otherwise disabled.
+          Also, hide it for system view in catalogs
+        */
+        var menu_enabled = function(itemData, item, data) {
+          var t = pgBrowser.tree, i = item, d = itemData;
+          var parent_item = t.hasParent(i) ? t.parent(i): null,
+              parent_data = parent_item ? t.itemData(parent_item) : null;
+            if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
+                if (_.indexOf(backup_supported_nodes, d._type) !== -1 &&
+                parent_data._type != 'catalog') {
+                    if (d._type == 'database' && d.allowConn)
+                      return true;
+                    else if(d._type != 'database')
+                      return true;
+                    else
+                      return false;
+                }
+                else
+                  return false;
+            }
+            else
+              return false;
+        };
+
+        var menu_enabled_server = function(itemData, item, data) {
+          var t = pgBrowser.tree, i = item, d = itemData;
+          var parent_item = t.hasParent(i) ? t.parent(i): null,
+              parent_data = parent_item ? t.itemData(parent_item) : null;
+              // If server node selected && connected
+              if(!_.isUndefined(d) && !_.isNull(d))
+                return (('server' === d._type) && d.connected);
+              else
+                false;
+        };
+
+        // Define the nodes on which the menus to be appear
+        var menus = [{
+          name: 'backup_global', module: this,
+          applies: ['tools'], callback: 'start_backup_global',
+          priority: 12, label: gettext('Backup Globals...'),
+          icon: 'fa fa-floppy-o', enable: menu_enabled_server
+        },{
+          name: 'backup_server', module: this,
+          applies: ['tools'], callback: 'start_backup_server',
+          priority: 12, label: gettext('Backup Server...'),
+          icon: 'fa fa-floppy-o', enable: menu_enabled_server
+        },{
+          name: 'backup_global_ctx', module: this, node: 'server',
+          applies: ['context'], callback: 'start_backup_global',
+          priority: 12, label: gettext('Backup Globals...'),
+          icon: 'fa fa-floppy-o', enable: menu_enabled_server
+        },{
+          name: 'backup_server_ctx', module: this, node: 'server',
+          applies: ['context'], callback: 'start_backup_server',
+          priority: 12, label: gettext('Backup Server...'),
+          icon: 'fa fa-floppy-o', enable: menu_enabled_server
+        },{
+          name: 'backup_object', module: this,
+          applies: ['tools'], callback: 'backup_objects',
+          priority: 11, label: gettext('Backup...'),
+          icon: 'fa fa-floppy-o', enable: menu_enabled
+        }];
+
+        for (var idx = 0; idx < backup_supported_nodes.length; idx++) {
+          menus.push({
+            name: 'backup_' + backup_supported_nodes[idx],
+            node: backup_supported_nodes[idx], module: this,
+            applies: ['context'], callback: 'backup_objects',
+            priority: 11, label: gettext('Backup...'),
+            icon: 'fa fa-floppy-o', enable: menu_enabled
+            });
+        }
+
+        pgAdmin.Browser.add_menus(menus);
+        return this;
+      },
+      start_backup_global: function(action, item) {
+        var params = {'globals': true };
+        this.start_backup_global_server.apply(
+          this, [action, item, params]
+        );
+      },
+      start_backup_server: function(action, item) {
+        var params = {'server': true };
+        this.start_backup_global_server.apply(
+          this, [action, item, params]
+        );
+      },
+
+      // Callback to draw Backup Dialog for globals/server
+      start_backup_global_server: function(action, item, params) {
+        var i = item || pgBrowser.tree.selected(),
+          server_data = null;
+
+        while (i) {
+          var node_data = pgBrowser.tree.itemData(i);
+          if (node_data._type == 'server') {
+            server_data = node_data;
+            break;
+          }
+
+          if (pgBrowser.tree.hasParent(i)) {
+            i = $(pgBrowser.tree.parent(i));
+          } else {
+            alertify.alert(gettext("Please select server or child node from the browser tree."));
+            break;
+          }
+        }
+
+        if (!server_data) {
+          return;
+        }
+
+        var module = 'paths',
+          preference_name = 'pg_bin_dir',
+          msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+        if ((server_data.type && server_data.type == 'ppas') ||
+            server_data.server_type == 'ppas') {
+          preference_name = 'ppas_bin_dir';
+          msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+        }
+
+        var preference = pgBrowser.get_preference(module, preference_name);
+
+        if(preference) {
+          if (!preference.value) {
+            alertify.alert(gettext('Configuration required'), msg);
+            return;
+          }
+        } else {
+          alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
+          return;
+        }
+
+        var of_type = undefined;
+
+        // Set Notes according to type of backup
+        if (!_.isUndefined(params['globals']) && params['globals']) {
+          of_type = 'globals';
+        } else {
+          of_type = 'server';
+        }
+
+        var DialogName = 'BackupDialog_' + of_type,
+            DialogTitle = ((of_type == 'globals') ?
+              gettext('Backup Globals...') :
+                gettext('Backup Server...'));
+
+        if(!alertify[DialogName]) {
+          alertify.dialog(DialogName ,function factory() {
+            return {
+               main: function(title) {
+                this.set('title', title);
+               },
+               build: function() {
+                alertify.pgDialogBuild.apply(this);
+               },
+               setup:function() {
+                return {
+                  buttons: [{
+                      text: '', className: 'btn btn-default pull-left fa fa-lg fa-info',
+                      attrs:{name:'object_help', type:'button', url: 'backup.html', label: gettext('Backup')}
+                    },{
+                      text: '', key: 112, className: 'btn btn-default pull-left fa fa-lg fa-question',
+                      attrs:{
+                        name:'dialog_help', type:'button', label: gettext('Backup'),
+                        url: url_for('help.static', {'filename': 'backup_dialog.html'})
+                      }
+                    },{
+                      text: gettext('Backup'), key: 13, className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                      'data-btn-name': 'backup'
+                    },{
+                      text: gettext('Cancel'), key: 27, className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                      'data-btn-name': 'cancel'
+                  }],
+                  // Set options for dialog
+                  options: {
+                    title: DialogTitle,
+                    //disable both padding and overflow control.
+                    padding : !1,
+                    overflow: !1,
+                    model: 0,
+                    resizable: true,
+                    maximizable: true,
+                    pinnable: false,
+                    closableByDimmer: false,
+                    modal: false
+                  }
+                };
+              },
+              hooks: {
+                // Triggered when the dialog is closed
+                onclose: function() {
+                  if (this.view) {
+                    // clear our backform model/view
+                    this.view.remove({data: true, internal: true, silent: true});
+                  }
+                }
+              },
+              prepare: function() {
+                var self = this;
+                // Disable Backup button until user provides Filename
+                this.__internal.buttons[2].element.disabled = true;
+
+                var $container = $("<div class='backup_dialog'></div>");
+                // Find current/selected node
+                var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (!d)
+                  return;
+                // Create treeInfo
+                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+                // Instance of backbone model
+                var newModel = new BackupModel(
+                  {type: of_type}, {node_info: treeInfo}
+                  ),
+                  fields = Backform.generateViewSchema(
+                    treeInfo, newModel, 'create', node, treeInfo.server, true
+                  );
+
+                var view = this.view = new Backform.Dialog({
+                  el: $container, model: newModel, schema: fields
+                });
+                // Add our class to alertify
+                $(this.elements.body.childNodes[0]).addClass(
+                  'alertify_tools_dialog_properties obj_properties'
+                );
+                // Render dialog
+                view.render();
+
+                this.elements.content.appendChild($container.get(0));
+
+                // Listen to model & if filename is provided then enable Backup button
+                this.view.model.on('change', function() {
+                    if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+                      this.errorModel.clear();
+                      self.__internal.buttons[2].element.disabled = false;
+                    } else {
+                      self.__internal.buttons[2].element.disabled = true;
+                      this.errorModel.set('file', gettext('Please provide a filename'))
+                    }
+                });
+              },
+              // Callback functions when click on the buttons of the Alertify dialogs
+              callback: function(e) {
+                // Fetch current server id
+                  var t = pgBrowser.tree,
+                    i = t.selected(),
+                    d = i && i.length == 1 ? t.itemData(i) : undefined,
+                    node = d && pgBrowser.Nodes[d._type];
+
+                if (e.button.element.name == "dialog_help" || e.button.element.name == "object_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    node, i, e.button.element.getAttribute('label'));
+                  return;
+                }
+
+                if (e.button['data-btn-name'] === "backup") {
+
+                  if (!d)
+                    return;
+
+                  var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                  var self = this,
+                    baseUrl = url_for('backup.create_server_job', {'sid': treeInfo.server._id}),
+                    args =  this.view.model.toJSON();
+
+                  $.ajax({
+                    url: baseUrl,
+                    method: 'POST',
+                    data:{ 'data': JSON.stringify(args) },
+                    success: function(res) {
+                      if (res.success) {
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.success(gettext('Backup job created.'), 5);
+                        pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+                      } else {
+                        console.log(res);
+                      }
+                    },
+                    error: function(xhr, status, error) {
+                      try {
+                        var err = $.parseJSON(xhr.responseText);
+                        alertify.alert(
+                          gettext('Backup job failed.'),
+                          err.errormsg
+                        );
+                      } catch (e) {}
+                    }
+                  });
+                }
+              }
+          };
+       });
+      }
+        alertify[DialogName](true).resizeTo('60%','50%');
+     },
+
+      // Callback to draw Backup Dialog for objects
+      backup_objects: function(action, treeItem) {
+
+        var i = treeItem || pgBrowser.tree.selected(),
+          server_data = null;
+
+        while (i) {
+          var node_data = pgBrowser.tree.itemData(i);
+          if (node_data._type == 'server') {
+            server_data = node_data;
+            break;
+          }
+
+          if (pgBrowser.tree.hasParent(i)) {
+            i = $(pgBrowser.tree.parent(i));
+          } else {
+            alertify.alert(gettext("Please select server or child node from tree."));
+            break;
+          }
+        }
+
+        if (!server_data) {
+          return;
+        }
+
+        var module = 'paths',
+          preference_name = 'pg_bin_dir',
+          msg = gettext('Please set binary path for PostgreSQL Server from preferences.');
+
+        if ((server_data.type && server_data.type == 'ppas') ||
+            server_data.server_type == 'ppas') {
+          preference_name = 'ppas_bin_dir';
+          msg = gettext('Please set binary path for EDB Postgres Advanced Server from preferences.');
+        }
+
+        var preference = pgBrowser.get_preference(module, preference_name);
+
+        if(preference) {
+          if (!preference.value) {
+            alertify.alert(msg);
+            return;
+          }
+        } else {
+          alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
+          return;
+        }
+
+        var title = S(gettext('Backup (%s: %s)')),
+            tree = pgBrowser.tree,
+            item = treeItem || tree.selected(),
+            data = item && item.length == 1 && tree.itemData(item),
+            node = data && data._type && pgBrowser.Nodes[data._type];
+
+        if (!node)
+          return;
+
+        title = title.sprintf(node.label, data.label).value();
+
+        if(!alertify.backup_objects) {
+          // Create Dialog title on the fly with node details
+          alertify.dialog('backup_objects' ,function factory() {
+            return {
+               main: function(title) {
+                this.set('title', title);
+               },
+               build: function() {
+                alertify.pgDialogBuild.apply(this);
+               },
+               setup:function() {
+                return {
+                  buttons: [{
+                      text: '', className: 'btn btn-default pull-left fa fa-lg fa-info',
+                      attrs:{name:'object_help', type:'button', url: 'backup.html', label: gettext('Backup')}
+                    },{
+                      text: '', key: 112, className: 'btn btn-default pull-left fa fa-lg fa-question',
+                      attrs:{
+                        name:'dialog_help', type:'button', label: gettext('Backup'),
+                        url: url_for('help.static', {'filename': 'backup_dialog.html'})
+                      }
+                    },{
+                      text: gettext('Backup'), key: 13, className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                      'data-btn-name': 'backup'
+                    },{
+                      text: gettext('Cancel'), key: 27, className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                      'data-btn-name': 'cancel'
+                  }],
+                  // Set options for dialog
+                  options: {
+                    title: title,
+                    //disable both padding and overflow control.
+                    padding : !1,
+                    overflow: !1,
+                    model: 0,
+                    resizable: true,
+                    maximizable: true,
+                    pinnable: false,
+                    closableByDimmer: false,
+                    modal: false
+                  }
+                };
+              },
+              hooks: {
+                // triggered when the dialog is closed
+                onclose: function() {
+                  if (this.view) {
+                    this.view.remove({data: true, internal: true, silent: true});
+                  }
+                }
+              },
+              prepare: function() {
+                var self = this;
+                // Disable Backup button until user provides Filename
+                this.__internal.buttons[2].element.disabled = true;
+                var $container = $("<div class='backup_dialog'></div>");
+                var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (!d)
+                  return;
+
+                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                var newModel = new BackupObjectModel(
+                  {}, {node_info: treeInfo}
+                  ),
+                  fields = Backform.generateViewSchema(
+                    treeInfo, newModel, 'create', node, treeInfo.server, true
+                  );
+
+                var view = this.view = new Backform.Dialog({
+                  el: $container, model: newModel, schema: fields
+                });
+
+                $(this.elements.body.childNodes[0]).addClass(
+                  'alertify_tools_dialog_properties obj_properties'
+                );
+
+                view.render();
+
+                this.elements.content.appendChild($container.get(0));
+
+                 // Listen to model & if filename is provided then enable Backup button
+                this.view.model.on('change', function() {
+                    if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+                      this.errorModel.clear();
+                      self.__internal.buttons[2].element.disabled = false;
+                    } else {
+                      self.__internal.buttons[2].element.disabled = true;
+                      this.errorModel.set('file', gettext('Please provide filename'))
+                    }
+                });
+
+              },
+              // Callback functions when click on the buttons of the Alertify dialogs
+              callback: function(e) {
+                // Fetch current server id
+                  var t = pgBrowser.tree,
+                    i = t.selected(),
+                    d = i && i.length == 1 ? t.itemData(i) : undefined,
+                    node = d && pgBrowser.Nodes[d._type];
+
+                if (e.button.element.name == "dialog_help" || e.button.element.name == "object_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    node, i, e.button.element.getAttribute('label'));
+                  return;
+                }
+
+                if (e.button['data-btn-name'] === "backup") {
+                  if (!d)
+                    return;
+
+                  var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                  // Set current database into model
+                  this.view.model.set('database', treeInfo.database._label);
+
+                  // We will remove once object tree is implemented
+                  // If selected node is Schema then add it in model
+                  if(d._type == 'schema') {
+                    var schemas = [];
+                    schemas.push(d._label);
+                    this.view.model.set('schemas', schemas);
+                  }
+                  // If selected node is Table then add it in model along with
+                  // its schema
+                  if(d._type == 'table') {
+                    this.view.model.set(
+                      'tables', [[treeInfo.schema._label, d._label]]
+                    );
+                  }
+
+                  var self = this,
+                    baseUrl = url_for('backup.create_object_job', {'sid': treeInfo.server._id}),
+                      args =  this.view.model.toJSON();
+
+                  $.ajax({
+                    url: baseUrl,
+                    method: 'POST',
+                    data:{ 'data': JSON.stringify(args) },
+                    success: function(res) {
+                      if (res.success) {
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.success(gettext('Backup job created.'), 5);
+                        pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+                      }
+                    },
+                    error: function(xhr, status, error) {
+                      try {
+                        var err = $.parseJSON(xhr.responseText);
+                        alertify.alert(
+                          gettext('Backup job failed.'),
+                          err.errormsg
+                        );
+                      } catch (e) {}
+                    }
+                  });
+                }
+              }
+            };
+          });
+        }
+        alertify.backup_objects(title).resizeTo('65%','60%');
+      }
+    };
+    return pgBrowser.Backup;
+  });
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index d3a4a9dc..3ff672a3 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -10,6 +10,7 @@
 """A blueprint module implementing the datagrid frame."""
 MODULE_NAME = 'datagrid'
 
+import os
 import simplejson as json
 import pickle
 import random
@@ -43,8 +44,9 @@ class DataGridModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.datagrid',
-            'path': url_for('datagrid.index') + "datagrid",
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/datagrid'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_panels(self):
@@ -66,7 +68,9 @@ class DataGridModule(PgAdminModule):
         ]
 
 
-blueprint = DataGridModule(MODULE_NAME, __name__, static_url_path='/static')
+blueprint = DataGridModule(
+    MODULE_NAME, __name__, static_url_path='/static'
+)
 
 
 @blueprint.route("/")
diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js
new file mode 100644
index 00000000..ff717174
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js
@@ -0,0 +1,536 @@
+define('pgadmin.datagrid', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'alertify', 'pgadmin',
+  'bundled_codemirror',
+  'sources/sqleditor_utils', 'wcdocker'
+], function(gettext, url_for, $, _, alertify, pgAdmin, CodeMirror, sqlEditorUtils) {
+    // Some scripts do export their object in the window only.
+    // Generally the one, which do no have AMD support.
+    var wcDocker = window.wcDocker,
+      pgBrowser = pgAdmin.Browser;
+
+    /* Return back, this has been called more than once */
+    if (pgAdmin.DataGrid)
+      return pgAdmin.DataGrid;
+
+    pgAdmin.DataGrid = {
+      init: function() {
+        if (this.initialized)
+            return;
+        this.initialized = true;
+        this.title_index = 1;
+
+        this.spinner_el = '<div class="wcLoadingContainer">'+
+              '<div class="wcLoadingBackground"></div>'+
+                '<div class="wcLoadingIconContainer">'+
+                  '<i class="wcLoadingIcon fa fa-spinner fa-pulse"></i>'+
+                '</div>'+
+              '</div>';
+        // Define list of nodes on which view data option appears
+        var supported_nodes = [
+           'table', 'view', 'mview',
+           'foreign-table', 'catalog_object', 'partition'
+        ],
+
+        /* Enable/disable View data menu in tools based
+         * on node selected. if selected node is present
+         * in supported_nodes, menu will be enabled
+         * otherwise disabled.
+         */
+        view_menu_enabled = function(obj) {
+          if(!_.isUndefined(obj) && !_.isNull(obj))
+            return (_.indexOf(supported_nodes, obj._type) !== -1 ? true: false);
+          else
+            return false;
+        },
+
+        /* Enable/disable Query tool menu in tools based
+         * on node selected. if selected node is present
+         * in unsupported_nodes, menu will be disabled
+         * otherwise enabled.
+         */
+        query_tool_menu_enabled = function(obj) {
+          if(!_.isUndefined(obj) && !_.isNull(obj)) {
+            if(_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
+              if (obj._type == 'database' && obj.allowConn)
+                return true;
+              else if(obj._type != 'database')
+                return true;
+              else
+                return false;
+             } else {
+                return false;
+             }
+          } else {
+            return false;
+          }
+        };
+
+        // Define the nodes on which the menus to be appear
+        var menus = [{
+          name: 'query_tool', module: this, applies: ['tools'],
+          callback: 'show_query_tool', enable: query_tool_menu_enabled,
+          priority: 1, label: gettext('Query Tool'),
+          icon: 'fa fa-bolt'
+        }];
+
+        // Create context menu
+        for (var idx = 0; idx < supported_nodes.length; idx++) {
+          menus.push({
+            name: 'view_all_rows_context_' + supported_nodes[idx],
+            node: supported_nodes[idx], module: this, data: {mnuid: 3},
+            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
+            category: 'view_data', priority: 101, label: gettext('All Rows')
+          },{
+            name: 'view_first_100_rows_context_' + supported_nodes[idx],
+            node: supported_nodes[idx], module: this, data: {mnuid: 1},
+            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
+            category: 'view_data', priority: 102, label: gettext('First 100 Rows')
+          },{
+            name: 'view_last_100_rows_context_' + supported_nodes[idx],
+            node: supported_nodes[idx], module: this, data: {mnuid: 2},
+            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
+            category: 'view_data', priority: 103, label: gettext('Last 100 Rows')
+          },{
+            name: 'view_filtered_rows_context_' + supported_nodes[idx],
+            node: supported_nodes[idx], module: this, data: {mnuid: 4},
+            applies: ['context', 'object'], callback: 'show_filtered_row', enable: view_menu_enabled,
+            category: 'view_data', priority: 104, label: gettext('Filtered Rows...')
+         });
+        }
+
+        pgAdmin.Browser.add_menu_category('view_data', gettext('View/Edit Data'), 100, 'fa fa-th');
+        pgAdmin.Browser.add_menus(menus);
+
+        // Creating a new pgAdmin.Browser frame to show the data.
+        var dataGridFrameType = new pgAdmin.Browser.Frame({
+          name: 'frm_datagrid',
+          showTitle: true,
+          isCloseable: true,
+          isPrivate: true,
+          url: 'about:blank'
+        });
+
+        // Load the newly created frame
+        dataGridFrameType.load(pgBrowser.docker);
+      },
+
+      // This is a callback function to show data when user click on menu item.
+      show_data_grid: function(data, i) {
+        var self = this,
+            d = pgAdmin.Browser.tree.itemData(i);
+        if (d === undefined) {
+          alertify.alert(
+            'Data Grid Error',
+            'No object selected.'
+          );
+          return;
+        }
+
+        // Get the parent data from the tree node hierarchy.
+        var node = pgBrowser.Nodes[d._type],
+          parentData = node.getTreeNodeHierarchy(i);
+
+        // If server, database or schema is undefined then return from the function.
+        if (parentData.server === undefined || parentData.database === undefined) {
+          return;
+        }
+        // If schema, view, catalog object all are undefined then return from the function.
+        if (parentData.schema === undefined && parentData.view === undefined &&
+             parentData.catalog === undefined) {
+          return;
+        }
+
+        var nsp_name = '';
+
+        if (parentData.schema != undefined) {
+            nsp_name = parentData.schema.label;
+        }
+        else if (parentData.view != undefined) {
+            nsp_name = parentData.view.label;
+        }
+        else if (parentData.catalog != undefined) {
+            nsp_name = parentData.catalog.label;
+        }
+        var url_params = {
+          'cmd_type': data.mnuid,
+          'obj_type': d._type,
+          'sid': parentData.server._id,
+          'did': parentData.database._id,
+          'obj_id': d._id
+        };
+
+        var baseUrl = url_for('datagrid.initialize_datagrid', url_params);
+        var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - '
+                        + nsp_name + '.' + d.label;
+
+        // Initialize the data grid.
+        self.initialize_data_grid(baseUrl, grid_title, '');
+      },
+
+      // This is a callback function to show filtered data when user click on menu item.
+      show_filtered_row: function(data, i) {
+        var self = this,
+            d = pgAdmin.Browser.tree.itemData(i);
+        if (d === undefined) {
+          alertify.alert(
+            'Data Grid Error',
+            'No object selected.'
+          );
+          return;
+        }
+
+        // Get the parent data from the tree node hierarchy.
+        var node = pgBrowser.Nodes[d._type],
+          parentData = node.getTreeNodeHierarchy(i);
+
+        // If server or database is undefined then return from the function.
+        if (parentData.server === undefined || parentData.database === undefined) {
+          return;
+        }
+
+        // If schema, view, catalog object all are undefined then return from the function.
+        if (parentData.schema === undefined && parentData.view === undefined &&
+             parentData.catalog === undefined) {
+          return;
+        }
+
+        var nsp_name = '';
+
+        if (parentData.schema != undefined) {
+            nsp_name = parentData.schema.label;
+        }
+        else if (parentData.view != undefined) {
+            nsp_name = parentData.view.label;
+        }
+        else if (parentData.catalog != undefined) {
+            nsp_name = parentData.catalog.label;
+        }
+
+        var url_params = {
+          'cmd_type': data.mnuid,
+          'obj_type': d._type,
+          'sid': parentData.server._id,
+          'did': parentData.database._id,
+          'obj_id': d._id
+
+        };
+
+        var baseUrl = url_for('datagrid.initialize_datagrid', url_params);
+
+        // Create url to validate the SQL filter
+        var validateUrl = url_for('datagrid.filter_validate', {
+                        'sid': url_params['sid'],
+                        'did': url_params['did'],
+                        'obj_id': url_params['obj_id'],
+                      });
+        var grid_title = parentData.server.label + '-' + parentData.database.label + '-'
+                        + nsp_name + '.' + d.label;
+
+        // Create filter dialog using alertify
+        if (!alertify.filterDialog) {
+          alertify.dialog('filterDialog', function factory() {
+            return {
+              main: function(title, message, baseUrl, validateUrl) {
+                this.set('title', title);
+                this.message = message;
+                this.baseUrl = baseUrl;
+                this.validateUrl = validateUrl;
+              },
+
+              setup:function() {
+                return {
+                  buttons:[
+                    { text: "OK", className: "btn btn-primary" },
+                    { text: "Cancel", className: "btn btn-danger" }
+                  ],
+                  options: { modal: 0, resizable: false, maximizable: false, pinnable: false}
+                };
+              },
+
+              build:function() {},
+              prepare:function() {
+		        var $content = $(this.message),
+                    $sql_filter = $content.find('#sql_filter');
+
+                this.setContent($content.get(0));
+
+                // Apply CodeMirror to filter text area.
+                this.filter_obj = codemirror.fromTextArea($sql_filter.get(0), {
+                  lineNumbers: true,
+                  indentUnit: 4,
+                  mode: "text/x-pgsql",
+                  extraKeys: pgBrowser.editor_shortcut_keys,
+                  tabSize: pgBrowser.editor_options.tabSize,
+                  lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
+                  autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
+                  matchBrackets: pgAdmin.Browser.editor_options.brace_matching
+                });
+              },
+
+              callback: function(closeEvent) {
+
+                if (closeEvent.button.text == gettext("OK")) {
+                  var sql = this.filter_obj.getValue();
+                  var that = this;
+
+                  // Make ajax call to include the filter by selection
+                  $.ajax({
+                    url: that.validateUrl,
+                    method: 'POST',
+                    async: false,
+                    contentType: "application/json",
+                    data: JSON.stringify(sql),
+                    success: function(res) {
+                      if (res.data.status) {
+                        // Initialize the data grid.
+                        self.initialize_data_grid(that.baseUrl, grid_title, sql);
+                      }
+                      else {
+                        alertify.alert(
+                          'Validation Error',
+                            res.data.result
+                        );
+                      }
+                    },
+                    error: function(e) {
+                      alertify.alert(
+                        'Validation Error',
+                        e
+                      );
+                    }
+                  });
+                }
+              }
+            };
+          });
+        }
+
+        var content = '';
+        $.get(url_for('datagrid.filter'),
+          function(data) {
+            alertify.filterDialog('Data Filter', data, baseUrl, validateUrl).resizeTo(600, 400);
+          }
+        );
+      },
+
+      get_panel_title: function() {
+        // Get the parent data from the tree node hierarchy.
+        var tree = pgAdmin.Browser.tree,
+            selected_item = tree.selected(),
+            item_data = tree.itemData(selected_item);
+        var self = this;
+
+        var node = pgBrowser.Nodes[item_data._type],
+          parentData = node.getTreeNodeHierarchy(selected_item);
+
+        // If server, database is undefined then return from the function.
+        if (parentData.server === undefined) {
+          return;
+        }
+        // If Database is not available then use default db
+        var db_label = parentData.database ? parentData.database.label
+                                           : parentData.server.db;
+
+        var grid_title = db_label + ' on ' + parentData.server.user.name + '@' +
+                parentData.server.label;
+        return grid_title;
+      },
+
+      initialize_data_grid: function(baseUrl, grid_title, sql_filter) {
+        var self = this;
+          self.grid_title = grid_title;
+
+        /* Ajax call to initialize the edit grid, which creates
+         * an asynchronous connection and create appropriate query
+         * for the selected node.
+         */
+        $.ajax({
+          url: baseUrl,
+          method: 'POST',
+          dataType: 'json',
+          contentType: "application/json",
+          data: JSON.stringify(sql_filter),
+          success: function(res) {
+
+            /* On successfully initialization find the dashboard panel,
+             * create new panel and add it to the dashboard panel.
+             */
+            var url_params = {
+              'trans_id': res.data.gridTransId,
+              'is_query_tool': 'false',
+              'editor_title': encodeURIComponent(self.grid_title)
+            };
+
+            var baseUrl = url_for('datagrid.panel', url_params);
+            var grid_title = gettext('Edit Data - ') + self.grid_title;
+            if (res.data.newBrowserTab) {
+              var newWin = window.open(baseUrl, '_blank');
+
+              // add a load listener to the window so that the title gets changed on page load
+              newWin.addEventListener("load", function() {
+                newWin.document.title = grid_title;
+              });
+            } else {
+              var propertiesPanel = pgBrowser.docker.findPanels('properties');
+              var dataGridPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);
+
+              // Set panel title and icon
+              dataGridPanel.title('<span title="'+grid_title+'">'+grid_title+'</span>');
+              dataGridPanel.icon('fa fa-bolt');
+              dataGridPanel.focus();
+
+              // Listen on the panel closed event.
+              dataGridPanel.on(wcDocker.EVENT.CLOSED, function() {
+                $.ajax({
+                  url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}),
+                  method: 'GET'
+                });
+              });
+
+              var openDataGridURL = function(j) {
+                // add spinner element
+                $(j).data('embeddedFrame').$container.append(self.spinner_el);
+                setTimeout(function() {
+                  var frameInitialized = $(j).data('frameInitialized');
+                  if (frameInitialized) {
+                    var frame = $(j).data('embeddedFrame');
+                    if (frame) {
+                      frame.openURL(baseUrl);
+                      frame.$container.find('.wcLoadingContainer').hide(1);
+                    }
+                  } else {
+                    openDataGridURL(j);
+                  }
+                }, 100);
+              };
+
+              openDataGridURL(dataGridPanel);
+            }
+          },
+          error: function(e) {
+            alertify.alert(
+              'SQL Tool Initialize Error'
+            );
+          }
+        });
+      },
+
+      // This is a callback function to show query tool when user click on menu item.
+      show_query_tool: function(url, i, panel_title) {
+        var self = this,
+          sURL = url || '',
+          panel_title = panel_title || '',
+          d = pgAdmin.Browser.tree.itemData(i);
+        if (d === undefined) {
+          alertify.alert(
+            'Query tool Error',
+            'No object selected.'
+          );
+          return;
+        }
+
+        // Get the parent data from the tree node hierarchy.
+        var node = pgBrowser.Nodes[d._type],
+          parentData = node.getTreeNodeHierarchy(i);
+
+        // If server, database is undefined then return from the function.
+        if (parentData.server === undefined) {
+          return;
+        }
+
+        var url_params = {
+          'sid': parentData.server._id
+        };
+        var url_endpoint = 'datagrid.initialize_query_tool'
+        // If database not present then use Maintenance database
+        // We will handle this at server side
+        if (parentData.database) {
+          url_params['did'] = parentData.database._id;
+          url_endpoint = 'datagrid.initialize_query_tool_with_did';
+        }
+        var baseUrl = url_for(url_endpoint, url_params);
+
+        $.ajax({
+          url: baseUrl,
+          method: 'POST',
+          dataType: 'json',
+          contentType: "application/json",
+          success: function(res) {
+            var grid_title = self.get_panel_title();
+            // Open the panel if frame is initialized
+            var url_params = {
+              'trans_id': res.data.gridTransId,
+              'is_query_tool': 'true',
+              'editor_title': encodeURIComponent(grid_title)
+            }
+
+            var baseUrl = url_for('datagrid.panel', url_params) +
+                '?' + "query_url=" + encodeURI(sURL);
+
+            // Create title for CREATE/DELETE scripts
+            if (panel_title) {
+              panel_title =
+                sqlEditorUtils.capitalizeFirstLetter(panel_title) + ' script';
+            }
+            else {
+              panel_title = gettext('Query - ') + grid_title;
+            }
+
+            if (res.data.newBrowserTab) {
+              var newWin = window.open(baseUrl, '_blank');
+
+              // add a load listener to the window so that the title gets changed on page load
+              newWin.addEventListener("load", function() {
+                newWin.document.title = panel_title;
+              });
+            } else {
+              /* On successfully initialization find the dashboard panel,
+               * create new panel and add it to the dashboard panel.
+               */
+              var propertiesPanel = pgBrowser.docker.findPanels('properties');
+              var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);
+
+              // Set panel title and icon
+              queryToolPanel.title('<span title="'+panel_title+'">'+panel_title+'</span>');
+              queryToolPanel.icon('fa fa-bolt');
+              queryToolPanel.focus();
+
+              // Listen on the panel closed event.
+              queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
+                $.ajax({
+                  url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}),
+                  method: 'GET'
+                });
+              });
+
+              var openQueryToolURL = function(j) {
+                // add spinner element
+                $(j).data('embeddedFrame').$container.append(pgAdmin.DataGrid.spinner_el);
+                setTimeout(function() {
+                  var frameInitialized = $(j).data('frameInitialized');
+                  if (frameInitialized) {
+                    var frame = $(j).data('embeddedFrame');
+                    if (frame) {
+                      frame.openURL(baseUrl);
+                      frame.$container.find('.wcLoadingContainer').delay(1000).hide(1);
+                    }
+                  } else {
+                    openQueryToolURL(j);
+                  }
+                }, 100);
+              };
+
+              openQueryToolURL(queryToolPanel);
+            }
+          },
+          error: function(e) {
+            alertify.alert(
+              gettext("Query Tool Initialize Error")
+            );
+          }
+        });
+      }
+    };
+
+    return pgAdmin.DataGrid;
+  });
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 46c6fd81..9043f77b 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -11,6 +11,7 @@
 
 MODULE_NAME = 'debugger'
 
+import os
 import simplejson as json
 import random
 
@@ -54,8 +55,9 @@ class DebuggerModule(PgAdminModule):
         ]:
             scripts.append({
                 'name': name,
-                'path': url_for('debugger.index') + script,
-                'when': None
+                'path': os.path.join(self.root_path, 'static/%s' % script),
+                'when': None,
+                'is_template': False
             })
 
         return scripts
diff --git a/web/pgadmin/tools/debugger/static/js/debugger.js b/web/pgadmin/tools/debugger/static/js/debugger.js
new file mode 100644
index 00000000..b791b2cc
--- /dev/null
+++ b/web/pgadmin/tools/debugger/static/js/debugger.js
@@ -0,0 +1,456 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror',
+  'backform', 'pgadmin.tools.debugger.ui', 'wcdocker', 'pgadmin.backform',
+  'pgadmin.backgrid', 'pgadmin.browser.frame'
+], function(gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform, get_function_arguments) {
+
+  pgAdmin = pgAdmin || window.pgAdmin || {};
+
+  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+  /* Return back, this has been called more than once */
+  if (pgAdmin.Tools.Debugger)
+    return pgAdmin.Tools.Debugger;
+
+  pgTools.Debugger = {
+      init: function() {
+        // We do not want to initialize the module multiple times.
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        // Initialize the context menu to display the debugging options when user open the context menu for functions
+        pgBrowser.add_menus([{
+          name: 'direct_debugger', node: 'function', module: this,
+          applies: ['object', 'context'], callback: 'get_function_information',
+          category: gettext('Debugging'), priority: 10, label: gettext('Debug'),
+          data: {object: 'function'}, icon: 'fa fa-arrow-circle-right',
+          enable: 'can_debug'
+        },{
+          name: 'global_debugger', node: 'function', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          category: gettext('Debugging'), priority: 10, label: gettext('Set breakpoint'),
+          data: {object: 'function', debug_type: 'indirect'},
+          icon: 'fa fa-arrow-circle-right', enable: 'can_debug'
+        },{
+          name: 'procedure_direct_debugger', node: 'procedure', module: this,
+          applies: ['object', 'context'], callback: 'get_function_information',
+          category: gettext('Debugging'), priority: 10, label: gettext('Debug'),
+          data: {object: 'procedure'}, icon: 'fa fa-arrow-circle-right',
+          enable: 'can_debug'
+        }, {
+          name: 'procedure_indirect_debugger', node: 'procedure', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          category: gettext('Debugging'), priority: 10, label: gettext('Set breakpoint'),
+          data: {object: 'procedure', debug_type: 'indirect'},
+          icon: 'fa fa-arrow-circle-right', enable: 'can_debug'
+        }, {
+          name: 'trigger_function_indirect_debugger', node: 'trigger_function', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          priority: 10, label: gettext('Set breakpoint'), category: gettext('Debugging'),
+          icon: 'fa fa-arrow-circle-right',
+          data: {object:'trigger_function', debug_type: 'indirect'}, enable: 'can_debug'
+        }, {
+          name: 'trigger_indirect_debugger', node: 'trigger', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          priority: 10, label: gettext('Set breakpoint'), category: gettext('Debugging'),
+          icon: 'fa fa-arrow-circle-right',
+          data: {object:'trigger', debug_type: 'indirect'}, enable: 'can_debug'
+        }, {
+          name: 'package_function_direct_debugger', node: 'edbfunc', module: this,
+          applies: ['object', 'context'], callback: 'get_function_information',
+          category: gettext('Debugging'), priority: 10, label: gettext('Debug'),
+          data: {object: 'edbfunc'}, icon: 'fa fa-arrow-circle-right',
+          enable: 'can_debug'
+        },{
+          name: 'package_function_global_debugger', node: 'edbfunc', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          category: gettext('Debugging'), priority: 10, label: gettext('Set breakpoint'),
+          data: {object: 'edbfunc', debug_type: 'indirect'},
+          icon: 'fa fa-arrow-circle-right', enable: 'can_debug'
+        },{
+          name: 'package_procedure_direct_debugger', node: 'edbproc', module: this,
+          applies: ['object', 'context'], callback: 'get_function_information',
+          category: gettext('Debugging'), priority: 10, label: gettext('Debug'),
+          data: {object: 'edbproc'}, icon: 'fa fa-arrow-circle-right',
+          enable: 'can_debug'
+        }, {
+          name: 'package_procedure_global_debugger', node: 'edbproc', module: this,
+          applies: ['object', 'context'], callback: 'check_func_debuggable',
+          category: gettext('Debugging'), priority: 10, label: gettext('Set breakpoint'),
+          data: {object: 'edbproc', debug_type: 'indirect'},
+          icon: 'fa fa-arrow-circle-right', enable: 'can_debug'
+        }]);
+
+        // Create and load the new frame required for debugger panel
+        this.frame = new pgBrowser.Frame({
+          name: 'frm_debugger',
+          title: gettext('Debugger'),
+          width: 500,
+          isCloseable: true,
+          isPrivate: true,
+          icon: 'fa fa-arrow-circle-right',
+          url: 'about:blank'
+        });
+
+        this.frame.load(pgBrowser.docker);
+      },
+      // It will check weather the function is actually debuggable or not with pre-required condition.
+      can_debug: function(itemData, item, data) {
+        var t = pgBrowser.tree, i = item, d = itemData;
+        // To iterate over tree to check parent node
+        while (i) {
+          if ('catalog' == d._type) {
+            //Check if we are not child of catalog
+            return false;
+          }
+          i = t.hasParent(i) ? t.parent(i) : null;
+          d = i ? t.itemData(i) : null;
+        }
+
+        // Find the function is really available in database
+        var tree = pgBrowser.tree,
+          info = tree.selected(),
+          d_ = info && info.length == 1 ? tree.itemData(info) : undefined,
+          node = d_ && pgBrowser.Nodes[d_._type];
+
+        if (!d_)
+          return false;
+
+        var treeInfo = node.getTreeNodeHierarchy.apply(node, [info]);
+
+        // For indirect debugging user must be super user
+        if(data && data.debug_type && data.debug_type == 'indirect'
+            && !treeInfo.server.user.is_superuser)
+          return false;
+
+        // Fetch object owner
+        var obj_owner = treeInfo.function && treeInfo.function.funcowner ||
+            treeInfo.procedure && treeInfo.procedure.funcowner ||
+            treeInfo.edbfunc && treeInfo.edbfunc.funcowner ||
+            treeInfo.edbproc && treeInfo.edbproc.funcowner;
+
+        // Must be a super user or object owner to create breakpoints of any kind
+        if (!(treeInfo.server.user.is_superuser || obj_owner == treeInfo.server.user.name))
+          return false;
+
+        // For trigger node, language will be undefined - we should allow indirect debugging for trigger node
+        if ((d_.language == undefined && d_._type == 'trigger') ||
+            (d_.language == undefined && d_._type == 'edbfunc') ||
+            (d_.language == undefined && d_._type == 'edbproc')) {
+          return true;
+        }
+
+        if (d_.language != 'plpgsql' && d_.language != 'edbspl') {
+           return false;
+        }
+
+        return true;
+      },
+      /*
+        For the direct debugging, we need to fetch the function information to display in the dialog so "generate_url"
+        will dynamically generate the URL from the server_id, database_id, schema_id and function id.
+      */
+      generate_url: function(_url, treeInfo, node) {
+        var url = '{BASEURL}{URL}/{OBJTYPE}{REF}',
+            ref = '';
+
+        _.each(
+          _.sortBy(
+            _.values(
+              _.pick(treeInfo,
+              function(v, k, o) {
+                return (k != 'server_group');
+              })
+            ),
+            function(o) { return o.priority; }
+          ),
+          function(o) {
+            ref = S('%s/%s').sprintf(ref, encodeURI(o._id)).value();
+          });
+
+        var args = {
+          'URL': _url,
+          'BASEURL': url_for('debugger.index'),
+          'REF': ref,
+          'OBJTYPE': encodeURI(node.type)
+        };
+
+        return url.replace(/{(\w+)}/g, function(match, arg) {
+          return args[arg];
+        });
+      },
+
+      check_func_debuggable: function(args, item) {
+        var input = args || {},
+          t = pgBrowser.tree,
+          i = item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined,
+          node = d && pgBrowser.Nodes[d._type];
+
+        if (!d)
+          return;
+
+        var objName = d.label,
+            treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+            _url = this.generate_url('init', treeInfo, node);
+
+        var self = this;
+        $.ajax({
+          url: _url,
+          cache: false,
+          success: function(res) {
+            self.start_global_debugger();
+          },
+          error: function(xhr, status, error) {
+            try {
+              var err = $.parseJSON(xhr.responseText);
+              if (err.success == 0) {
+                Alertify.alert(err.errormsg);
+              }
+            } catch (e) {}
+          }
+        });
+      },
+
+      //Callback function when user start the indirect debugging ( Listen to another session to invoke the target )
+      start_global_debugger: function(args, item) {
+        // Initialize the target and create asynchronous connection and unique transaction ID
+        var t = pgBrowser.tree,
+          i = t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined,
+          node = d && pgBrowser.Nodes[d._type];
+
+        if (!d)
+          return;
+
+        var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+        if (d._type == "function") {
+          var baseUrl = url_for(
+                          'debugger.initialize_target_for_function', {
+                            'debug_type': 'indirect',
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.function._id
+                          }
+                        );
+        }
+        else if (d._type == "procedure") {
+          var baseUrl = url_for(
+                          'debugger.initialize_target_for_function', {
+                            'debug_type': 'indirect',
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.procedure._id
+                          }
+                        );
+        }
+        else if (d._type == "trigger_function") {
+          var baseUrl = url_for(
+                          'debugger.initialize_target_for_function', {
+                            'debug_type': 'indirect',
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.trigger_function._id
+                          }
+                        );
+        }
+        else if (d._type == "trigger" && "table" in treeInfo) {
+          var baseUrl = url_for(
+                          'debugger.initialize_target_for_trigger', {
+                            'debug_type': 'indirect',
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.table._id,
+                            'tri_id': treeInfo.trigger._id
+                          }
+                        );
+        }
+        else if (d._type == "trigger" && "view" in treeInfo) {
+          var baseUrl = url_for(
+                          'debugger.initialize_target_for_trigger', {
+                            'debug_type': 'indirect',
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.view._id,
+                            'tri_id': treeInfo.trigger._id
+                          }
+                        );
+        }
+
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            var url = url_for('debugger.direct', {'trans_id': res.data.debuggerTransId});
+
+            if (res.data.newBrowserTab) {
+              window.open(url, '_blank');
+            } else {
+              pgBrowser.Events.once(
+                'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+                frame.openURL(url);
+              });
+
+              // Create the debugger panel as per the data received from user input dialog.
+              var dashboardPanel = pgBrowser.docker.findPanels(
+                'properties'
+              ),
+              panel = pgBrowser.docker.addPanel(
+                'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+              );
+
+              panel.focus();
+
+              // Panel Closed event
+              panel.on(wcDocker.EVENT.CLOSED, function() {
+                var closeUrl = url_for('debugger.close', {'trans_id': res.data.debuggerTransId});
+                $.ajax({
+                  url: closeUrl,
+                  method: 'DELETE'
+                });
+              });
+            }
+          },
+          error: function(xhr, status, error) {
+            try {
+              var err = $.parseJSON(xhr.responseText);
+              if (err.success == 0) {
+                Alertify.alert(err.errormsg);
+              }
+            } catch (e) {}
+          }
+        });
+      },
+
+      /*
+        Get the function information for the direct debugging to display the functions arguments and  other informations
+        in the user input dialog
+      */
+      get_function_information: function(args, item) {
+        var input = args || {},
+          t = pgBrowser.tree,
+          i = item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined,
+          node = d && pgBrowser.Nodes[d._type];
+
+        if (!d)
+          return;
+
+        var objName = d.label,
+            treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+            _url = this.generate_url('init', treeInfo, node);
+
+        var self = this;
+        $.ajax({
+          url: _url,
+          cache: false,
+          success: function(res) {
+
+            // Open Alertify the dialog to take the input arguments from user if function having input arguments
+            if (res.data[0]['require_input']) {
+              get_function_arguments(res.data[0], 0);
+            }
+            else {
+              // Initialize the target and create asynchronous connection and unique transaction ID
+              // If there is no arguments to the functions then we should not ask for for function arguments and
+              // Directly open the panel
+              var t = pgBrowser.tree,
+                i = t.selected(),
+                d = i && i.length == 1 ? t.itemData(i) : undefined,
+                node = d && pgBrowser.Nodes[d._type];
+
+              if (!d)
+                return;
+
+              var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+              if (d._type == "function") {
+                var baseUrl = url_for(
+                                'debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.function._id
+                                }
+                              );
+              }
+              else {
+                var baseUrl = url_for(
+                                'debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.procedure._id
+                                }
+                              );
+              }
+
+              $.ajax({
+                url: baseUrl,
+                method: 'GET',
+                success: function(res) {
+
+                  var url = url_for('debugger.direct', {'trans_id': res.data.debuggerTransId});
+
+                  if (res.data.newBrowserTab) {
+                    window.open(url, '_blank');
+                  } else {
+                    pgBrowser.Events.once(
+                      'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+                      frame.openURL(url);
+                    });
+
+                    // Create the debugger panel as per the data received from user input dialog.
+                    var dashboardPanel = pgBrowser.docker.findPanels(
+                      'properties'
+                      ),
+                      panel = pgBrowser.docker.addPanel(
+                        'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+                        );
+
+                    panel.focus();
+
+                    // Register Panel Closed event
+                    panel.on(wcDocker.EVENT.CLOSED, function() {
+                      var closeUrl = url_for('debugger.close', {'trans_id': res.data.debuggerTransId});
+                      $.ajax({
+                        url: closeUrl,
+                        method: 'DELETE'
+                      });
+                    });
+                  }
+                },
+                error: function(e) {
+                  Alertify.alert(
+                    'Debugger target Initialize Error',
+                    e.responseJSON.errormsg
+                  );
+                }
+              });
+            }
+          },
+          error: function(xhr, status, error) {
+            try {
+              var err = $.parseJSON(xhr.responseText);
+              if (err.success == 0) {
+                Alertify.alert('Debugger Error', err.errormsg);
+              }
+            } catch (e) {}
+          }
+        });
+      }
+    };
+
+    return pgAdmin.Tools.Debugger;
+  });
diff --git a/web/pgadmin/tools/debugger/static/js/debugger_ui.js b/web/pgadmin/tools/debugger/static/js/debugger_ui.js
new file mode 100644
index 00000000..8c7076c5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/static/js/debugger_ui.js
@@ -0,0 +1,785 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror',
+  'backform', 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid',
+  'pgadmin.browser.panel'
+], function(gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform ) {
+
+  /*
+   * Function used to return the respective Backgrid control based on the data type
+   * of function input argument.
+   */
+  var cellFunction = function(model) {
+    var self = this,
+    variable_type = model.get("type");
+
+    // if variable type is an array then we need to render the custom control to take the input from user.
+    if (variable_type.indexOf("[]") !=-1) {
+      if (variable_type.indexOf("integer") != -1) {
+        return Backgrid.Extension.InputIntegerArrayCell;
+      }
+      return Backgrid.Extension.InputStringArrayCell;
+    }
+
+    switch(variable_type) {
+      case "bool":
+        return Backgrid.BooleanCell;
+        break;
+
+      case "integer":
+        // As we are getting this value as text from sqlite database so we need to type cast it.
+        if (model.get('value') != undefined) {
+          model.set({'value': parseInt(model.get('value'))},{silent:true});
+        }
+
+        return Backgrid.IntegerCell;
+        break;
+
+      case "real":
+        // As we are getting this value as text from sqlite database so we need to type cast it.
+        if (model.get('value') != undefined) {
+          model.set({'value': parseFloat(model.get('value'))} ,{silent:true});
+        }
+        return Backgrid.NumberCell;
+        break;
+
+      case "string":
+        return Backgrid.StringCell;
+        break;
+      case "date":
+        return Backgrid.DateCell;
+        break;
+      default:
+        return Backgrid.Cell;
+        break;
+    }
+  }
+
+  /*
+   * Function used to return the respective Backgrid string or boolean control based on the data type
+   * of function input argument.
+   */
+  var cellExprControlFunction = function(model) {
+    var self = this,
+    variable_type = model.get("type");
+    if (variable_type.indexOf("[]") !=-1) {
+      return Backgrid.StringCell;
+    }
+    return Backgrid.BooleanCell;
+  }
+
+  /**
+   *  DebuggerInputArgsModel used to represent input parameters for the function to debug
+   *  for function objects.
+   **/
+  var DebuggerInputArgsModel = Backbone.Model.extend({
+    defaults: {
+      name: undefined,
+      type: undefined,
+      is_null: undefined,
+      expr: undefined,
+      value: undefined,
+      use_default: undefined,
+      default_value: undefined,
+    },
+    validate: function() {
+      if (_.isUndefined(this.get('value')) ||
+          _.isNull(this.get('value')) ||
+          String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+        var msg = gettext('Please enter a value for the parameter.');
+        this.errorModel.set('value', msg);
+        return msg;
+      } else {
+        this.errorModel.unset('value');
+      }
+      return null;
+    }
+  });
+
+  // Collection which contains the model for function informations.
+  var DebuggerInputArgCollections = Backbone.Collection.extend({
+    model: DebuggerInputArgsModel
+  });
+
+  // function will enable/disable the use_default column based on the value received.
+  var disableDefaultCell = function(d) {
+    if (d instanceof Backbone.Model) {
+        return d.get('use_default');
+    }
+    return false;
+  };
+
+  // Enable/Disable the control based on the array data type of the function input arguments
+  var disableExpressionControl = function(d) {
+    var argType = d.get('type');
+    if (d instanceof Backbone.Model) {
+      var argType = d.get('type');
+      if (argType.indexOf("[]") !=-1) {
+        return false;
+      }
+      return true;
+    }
+  };
+
+  var res =  function(args, restart_debug) {
+    if (!Alertify.debuggerInputArgsDialog) {
+      Alertify.dialog('debuggerInputArgsDialog', function factory() {
+        return {
+          main:function(title, data, restart_debug) {
+            this.set('title', title);
+            this.data = data;
+            this.restart_debug = restart_debug;
+
+            // Variables to store the data sent from sqlite database
+            var func_args_data = this.func_args_data = [];
+
+            // As we are not getting pgBrowser.tree when we debug again so tree info will be updated from the server data
+            if (restart_debug == 0) {
+              var t = pgBrowser.tree,
+                i = t.selected(),
+                d = i && i.length == 1 ? t.itemData(i) : undefined,
+                node = d && pgBrowser.Nodes[d._type];
+
+              if (!d)
+                return;
+
+              var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+              if (d._type == "function") {
+                // Get the existing function parameters available from sqlite database
+                var _Url = url_for('debugger.get_arguments', {
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.function._id
+                           });
+              }
+              else if (d._type == "procedure") {
+                // Get the existing function parameters available from sqlite database
+                var _Url = url_for('debugger.get_arguments', {
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.procedure._id
+                           });
+              }
+              else if (d._type == "edbfunc") {
+                // Get the existing function parameters available from sqlite database
+                var _Url = url_for('debugger.get_arguments', {
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.edbfunc._id
+                           });
+              }
+              else if (d._type == "edbproc") {
+                // Get the existing function parameters available from sqlite database
+                var _Url = url_for('debugger.get_arguments', {
+                            'sid': treeInfo.server._id,
+                            'did': treeInfo.database._id,
+                            'scid': treeInfo.schema._id,
+                            'func_id': treeInfo.edbproc._id
+                           });
+              }
+            }
+            else {
+              // Get the existing function parameters available from sqlite database
+              var _Url = url_for('debugger.get_arguments', {
+                          'sid': this.data.server_id,
+                          'did': this.data.database_id,
+                          'scid': this.data.schema_id,
+                          'func_id': this.data.function_id
+                         });
+            }
+            $.ajax({
+              url: _Url,
+              method: 'GET',
+              async: false,
+              success: function(res) {
+                if (res.data.args_count != 0) {
+                  for (i = 0; i < res.data.result.length; i++) {
+                    // Below will format the data to be stored in sqlite database
+                    func_args_data.push({
+                      'arg_id': res.data.result[i]['arg_id'],
+                      'is_null': res.data.result[i]['is_null'],
+                      'is_expression': res.data.result[i]['is_expression'],
+                      'use_default': res.data.result[i]['use_default'],
+                      'value': res.data.result[i]['value']
+                    });
+                  }
+                }
+              },
+              error: function(e) {
+                Alertify.alert(
+                  'Debugger Set arguments error'
+                );
+              }
+            });
+
+            var argname, argtype, argmode, default_args_count, default_args, arg_cnt;
+
+            var value_header = Backgrid.HeaderCell.extend({
+                // Add fixed width to the "value" column
+                className: 'width_percent_25'
+            });
+
+            var def_val_list = [],
+            gridCols = [
+              {name: 'name', label:'Name', type:'text', editable: false, cell:'string' },
+              {name: 'type', label:'Type', type: 'text', editable: false, cell:'string' },
+              {name: 'is_null', label:'Null?', type: 'boolean', cell: 'boolean' },
+              {name: 'expr', label:'Expression?', type: 'boolean', cellFunction: cellExprControlFunction, editable: disableExpressionControl },
+              {name: 'value', label:'Value', type: 'text', editable: true, cellFunction: cellFunction, headerCell: value_header },
+              {name: 'use_default', label:'Use Default?', type: 'boolean', cell:"boolean", editable: disableDefaultCell },
+              {name: 'default_value', label:'Default value', type: 'text', editable: false, cell:'string' }
+            ];
+
+            var my_obj = [];
+            var func_obj = [];
+
+            // Below will calculate the input argument id required to store in sqlite database
+            var input_arg_id = this.input_arg_id = [];
+            if (this.data['proargmodes'] != null) {
+              var argmode_1 = this.data['proargmodes'].split(",");
+              for (var k = 0; k < argmode_1.length; k++) {
+                if (argmode_1[k] == 'i' || argmode_1[k] == 'b') {
+                  input_arg_id.push(k)
+                }
+              }
+            }
+            else {
+              var argtype_1 = this.data['proargtypenames'].split(",");
+              for (var k = 0; k < argtype_1.length; k++) {
+                  input_arg_id.push(k)
+              }
+            }
+
+            argtype = this.data['proargtypenames'].split(",");
+
+            if (this.data['proargmodes'] != null) {
+                argmode = this.data['proargmodes'].split(",");
+            }
+
+            if (this.data['pronargdefaults']) {
+              default_args_count = this.data['pronargdefaults'];
+              default_args = this.data['proargdefaults'].split(",");
+              arg_cnt = default_args_count;
+            }
+
+            if (this.data['proargnames'] != null) {
+              argname = this.data['proargnames'].split(",");
+
+              // It will assign default values to "Default value" column
+              for (var j = (argname.length - 1); j >= 0; j--) {
+                if (this.data['proargmodes'] != null) {
+                  if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+                    arg_cnt = arg_cnt - 1;
+                    def_val_list[j] = default_args[arg_cnt]
+                  }
+                  else {
+                    def_val_list[j] = "<No default value>";
+                  }
+                }
+                else {
+                  if (arg_cnt) {
+                    arg_cnt = arg_cnt - 1;
+                    def_val_list[j] = default_args[arg_cnt]
+                  }
+                  else {
+                    def_val_list[j] = "<No default value>";
+                  }
+                }
+              }
+
+              if (argtype.length != 0)
+              {
+                for (i = 0; i < argtype.length; i++) {
+                  if (this.data['proargmodes'] != null) {
+                    if (argmode[i] == 'i' || argmode[i] == 'b') {
+                      var use_def_value = false
+                      if (def_val_list[i] != "<No default value>") {
+                        use_def_value = true;
+                      }
+                      my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+                    }
+                  }
+                  else {
+                    var use_def_value = false
+                    if (def_val_list[i] != "<No default value>") {
+                      use_def_value = true;
+                    }
+                    my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+                  }
+
+
+                }
+              }
+
+              // Need to update the func_obj variable from sqlite database if available
+              if (func_args_data.length != 0) {
+                for (i = 0; i < func_args_data.length; i++) {
+                  var index = func_args_data[i]['arg_id'];
+                  var values = [];
+                  if (argtype[index].indexOf("[]") !=-1) {
+                      var vals = func_args_data[i]['value'].split(",");
+                      if (argtype[index].indexOf("integer") != -1) {
+                        _.each(vals, function(val){
+                            values.push({'value': parseInt(val)});
+                        });
+                      }
+                      _.each(vals, function(val){
+                            values.push({'value': val});
+                        });
+                  } else {
+                    values = func_args_data[i]['value'];
+                  }
+
+                  func_obj.push({ "name": argname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": values, "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+                }
+              }
+            }
+            else {
+              /*
+               Generate the name parameter if function do not have arguments name
+               like dbgparam1, dbgparam2 etc.
+              */
+              var myargname = [];
+
+              for (i = 0; i < argtype.length; i++) {
+                myargname[i] = "dbgparam" + (i+1);
+              }
+
+              // If there is no default arguments
+              if (!this.data['pronargdefaults']) {
+                for (i = 0; i < argtype.length; i++) {
+                  my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": false, "default_value": "<No default value>"});
+                  def_val_list[i] = "<No default value>";
+                }
+              }
+              else {
+                // If there is default arguments
+                //Below logic will assign default values to "Default value" column
+                for (var j = (myargname.length - 1);j >= 0; j--) {
+                  if (this.data['proargmodes'] == null) {
+                    if (arg_cnt) {
+                      arg_cnt = arg_cnt - 1;
+                      def_val_list[j] = default_args[arg_cnt]
+                    }
+                    else {
+                      def_val_list[j] = "<No default value>";
+                    }
+                  }
+                  else {
+                    if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+                      arg_cnt = arg_cnt - 1;
+                      def_val_list[j] = default_args[arg_cnt]
+                    }
+                    else {
+                      def_val_list[j] = "<No default value>";
+                    }
+                  }
+                }
+
+                for (i = 0; i < argtype.length; i++) {
+                  if (this.data['proargmodes'] == null) {
+                    var use_def_value = false
+                    if (def_val_list[i] != "<No default value>") {
+                      use_def_value = true;
+                    }
+                    my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+                  }
+                  else {
+                    if (argmode[i] == 'i' || argmode[i] == 'b') {
+                      var use_def_value = false
+                      if (def_val_list[i] != "<No default value>") {
+                        use_def_value = true;
+                      }
+                      my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+                    }
+                  }
+                }
+              }
+
+              // Need to update the func_obj variable from sqlite database if available
+              if (func_args_data.length != 0) {
+                for (i = 0; i < func_args_data.length; i++) {
+                  var index = func_args_data[i]['arg_id'];
+                  var values = [];
+                  if (argtype[index].indexOf("[]") !=-1) {
+                      var vals = func_args_data[i]['value'].split(",");
+                      if (argtype[index].indexOf("integer") != -1) {
+                        _.each(vals, function(val){
+                            values.push({'value': parseInt(val)});
+                        });
+                      }
+                      _.each(vals, function(val){
+                            values.push({'value': val});
+                        });
+                  } else {
+                    values = func_args_data[i]['value'];
+                  }
+                  func_obj.push({ "name": myargname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": values, "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+                }
+              }
+            }
+
+            // Check if the arguments already available in the sqlite database then we should use the existing arguments
+            if (func_args_data.length == 0) {
+              var debuggerInputArgsColl = this.debuggerInputArgsColl = new DebuggerInputArgCollections(my_obj);
+            }
+            else {
+              var debuggerInputArgsColl = this.debuggerInputArgsColl = new DebuggerInputArgCollections(func_obj);
+            }
+
+            // Initialize a new Grid instance
+            if (this.grid) {
+                this.grid.remove();
+                this.grid = null;
+            }
+            var grid = this.grid = new Backgrid.Grid({
+              columns: gridCols,
+              collection: debuggerInputArgsColl,
+              className: "backgrid table-bordered"
+            });
+
+            grid.render();
+            $(this.elements.content).html(grid.el);
+          },
+          setup:function() {
+            return {
+              buttons:[{ text: "Debug", key: 13, className: "btn btn-primary" },
+                       { text: "Cancel", key: 27, className: "btn btn-primary" }],
+              options: { modal: 0, resizable: true }
+            };
+          },
+          // Callback functions when click on the buttons of the Alertify dialogs
+          callback: function(e) {
+            if (e.button.text === "Debug") {
+
+              // Initialize the target once the debug button is clicked and
+              // create asynchronous connection and unique transaction ID
+              var self = this;
+
+              // If the debugging is started again then treeInfo is already stored in this.data so we can use the same.
+              if (self.restart_debug == 0) {
+                var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (!d)
+                  return;
+
+                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+              }
+
+              var args_value_list = [];
+              var sqlite_func_args_list = this.sqlite_func_args_list = [];
+              var int_count = 0;
+
+              this.grid.collection.each(function(m) {
+
+                  // Check if value is set to NULL then we should ignore the value field
+                  if (m.get('is_null')) {
+                    args_value_list.push({ 'name': m.get('name'),
+                                       'type': m.get('type'),
+                                       'value': 'NULL'});
+                  }
+                  else {
+                    // Check if default value to be used or not
+                    if (m.get('use_default')) {
+                      args_value_list.push({ 'name': m.get('name'),
+                                       'type': m.get('type'),
+                                       'value': m.get('default_value')});
+                    }
+                    else {
+                      args_value_list.push({ 'name': m.get('name'),
+                                       'type': m.get('type'),
+                                       'value': m.get('value')});
+                    }
+                  }
+
+                  if (self.restart_debug == 0) {
+                    var f_id;
+                    if (d._type == "function") {
+                      f_id = treeInfo.function._id;
+                    }
+                    else if (d._type == "procedure") {
+                      f_id = treeInfo.procedure._id;
+                    }
+                    else if (d._type == "edbfunc") {
+                      f_id = treeInfo.edbfunc._id;
+                    }
+                    else if (d._type == "edbproc") {
+                      f_id = treeInfo.edbproc._id;
+                    }
+
+                    // Below will format the data to be stored in sqlite database
+                    sqlite_func_args_list.push({
+                      'server_id': treeInfo.server._id,
+                      'database_id': treeInfo.database._id,
+                      'schema_id': treeInfo.schema._id ,
+                      'function_id': f_id,
+                      'arg_id': self.input_arg_id[int_count],
+                      'is_null': m.get('is_null')  ? 1 : 0,
+                      'is_expression': m.get('expr') ? 1 : 0,
+                      'use_default': m.get('use_default') ? 1 : 0,
+                      'value': m.get('value')
+                    });
+                  }
+                  else {
+                    // Below will format the data to be stored in sqlite database
+                    sqlite_func_args_list.push({
+                      'server_id': self.data.server_id,
+                      'database_id': self.data.database_id,
+                      'schema_id': self.data.schema_id ,
+                      'function_id': self.data.function_id,
+                      'arg_id': self.input_arg_id[int_count],
+                      'is_null': m.get('is_null')  ? 1 : 0,
+                      'is_expression': m.get('expr') ? 1 : 0,
+                      'use_default': m.get('use_default') ? 1 : 0,
+                      'value': m.get('value')
+                    });
+                  }
+
+                  int_count = int_count + 1;
+              });
+
+              // If debugging is not started again then we should initialize the target otherwise not
+              if (self.restart_debug == 0) {
+                var baseUrl;
+                if (d._type == "function") {
+                  baseUrl = url_for('debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.function._id
+                                });
+                }
+                else if (d._type == "procedure") {
+                  baseUrl = url_for('debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.procedure._id
+                                });
+                }
+                else if (d._type == "edbfunc") {
+                  baseUrl = url_for('debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.edbfunc._id
+                                });
+                }
+                else if (d._type == "edbproc") {
+                  baseUrl = url_for('debugger.initialize_target_for_function', {
+                                  'debug_type': 'direct',
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.edbproc._id
+                                });
+                }
+
+                $.ajax({
+                  url: baseUrl,
+                  method: 'POST',
+                  data:{'data':JSON.stringify(args_value_list)},
+                  success: function(res) {
+
+                    var url = url_for('debugger.direct', {'trans_id': res.data.debuggerTransId});
+
+                    if (res.data.newBrowserTab) {
+                      window.open(url, '_blank');
+                    } else {
+                      pgBrowser.Events.once(
+                        'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+                        frame.openURL(url);
+                      });
+
+                      // Create the debugger panel as per the data received from user input dialog.
+                      var dashboardPanel = pgBrowser.docker.findPanels('properties'),
+                          panel = pgBrowser.docker.addPanel(
+                            'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+                          );
+
+                      panel.focus();
+
+                      // Panel Closed event
+                      panel.on(wcDocker.EVENT.CLOSED, function() {
+                        var closeUrl = url_for('debugger.close', {'trans_id': res.data.debuggerTransId});
+                        $.ajax({
+                          url: closeUrl,
+                          method: 'DELETE'
+                        });
+                      });
+                    }
+
+                    if (d._type == "function") {
+                      var _Url = url_for('debugger.set_arguments', {
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.function._id,
+                                 });
+                    }
+                    else if (d._type == "procedure") {
+                      var _Url = url_for('debugger.set_arguments', {
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.procedure._id,
+                                 });
+                    }
+                    else if (d._type == "edbfunc") {
+                      // Get the existing function parameters available from sqlite database
+                      var _Url = url_for('debugger.set_arguments', {
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.edbfunc._id,
+                                 });
+                    }
+                    else if (d._type == "edbproc") {
+                      // Get the existing function parameters available from sqlite database
+                      var _Url = url_for('debugger.set_arguments', {
+                                  'sid': treeInfo.server._id,
+                                  'did': treeInfo.database._id,
+                                  'scid': treeInfo.schema._id,
+                                  'func_id': treeInfo.edbproc._id,
+                                 });
+                    }
+
+                    $.ajax({
+                      url: _Url,
+                      method: 'POST',
+                      data:{'data':JSON.stringify(sqlite_func_args_list)},
+                      success: function(res) {
+                      },
+                      error: function(e) {
+                        Alertify.alert(
+                          'Debugger Set arguments error'
+                        );
+                      }
+                    });
+                  },
+                  error: function(e) {
+                    Alertify.alert(
+                      'Debugger target Initialize Error',
+                      e.responseJSON.errormsg
+                    );
+                  }
+                });
+              }
+              else {
+                // If the debugging is started again then we should only set the arguments and start the listener again
+                var baseUrl = url_for('debugger.start_listener', {'trans_id': self.data.trans_id});
+
+                $.ajax({
+                  url: baseUrl,
+                  method: 'POST',
+                  data:{'data':JSON.stringify(args_value_list)},
+                  success: function(res) {
+                  },
+                  error: function(e) {
+                    Alertify.alert(
+                      'Debugger listener starting error',
+                      e.responseJSON.errormsg
+                    );
+                  }
+                });
+
+                // Set the new input arguments given by the user during debugging
+                var _Url = url_for('debugger.set_arguments', {
+                            'sid': self.data.server_id,
+                            'did': self.data.database_id,
+                            'scid': self.data.schema_id,
+                            'func_id': self.data.function_id
+                           });
+                $.ajax({
+                  url: _Url,
+                  method: 'POST',
+                  data:{'data':JSON.stringify(sqlite_func_args_list)},
+                  success: function(res) {
+                  },
+                  error: function(e) {
+                    Alertify.alert(
+                      'Debugger Set arguments error'
+                    );
+                  }
+                });
+
+              }
+
+              return true;
+            }
+
+            if (e.button.text === "Cancel") {
+              //close the dialog...
+              return false;
+            }
+          },
+          build:function() {
+          },
+          prepare:function() {
+            /*
+             If we already have data available in sqlite database then we should enable the debug button otherwise
+             disable the debug button.
+            */
+            if (this.func_args_data.length == 0) {
+              this.__internal.buttons[0].element.disabled = true;
+            }
+            else {
+              this.__internal.buttons[0].element.disabled = false;
+            }
+
+            /*
+             Listen to the grid change event so that if any value changed by user then we can enable/disable the
+             debug button.
+            */
+            this.grid.listenTo(this.debuggerInputArgsColl,"backgrid:edited",
+              (function(obj) {
+
+                return function() {
+
+                  var self = this;
+                  var enable_btn = false;
+
+                  for (var    i = 0; i < this.collection.length; i++ ) {
+
+                    // TODO: Need to check the "NULL" and "Expression" column value to enable/disable the "Debug" button
+                    if (this.collection.models[i].get('value') == "" ||
+                        this.collection.models[i].get('value') == null ||
+                        this.collection.models[i].get('value') == undefined) {
+                          enable_btn = true;
+
+                          if (this.collection.models[i].get('use_default')) {
+                            obj.__internal.buttons[0].element.disabled = false;
+                          }
+                          else{
+                            obj.__internal.buttons[0].element.disabled = true;
+                            break;
+                          }
+                    }
+                  }
+                  if (!enable_btn)
+                    obj.__internal.buttons[0].element.disabled = false;
+                }
+              }
+              )(this)
+            );
+          }
+        };
+      });
+    }
+
+    Alertify.debuggerInputArgsDialog('Debugger',args, restart_debug).resizeTo('60%', '60%');
+
+  };
+
+  return res;
+});
diff --git a/web/pgadmin/tools/debugger/static/js/direct.js b/web/pgadmin/tools/debugger/static/js/direct.js
new file mode 100644
index 00000000..7dd72fee
--- /dev/null
+++ b/web/pgadmin/tools/debugger/static/js/direct.js
@@ -0,0 +1,1699 @@
+define([
+  'sources/gettext', 'sources/url_for' ,'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin','pgadmin.browser', 'backbone', 'backgrid', 'sources/../bundle/codemirror', 'backform',
+  'pgadmin.tools.debugger.ui',
+  'sources/alerts/alertify_wrapper',
+
+  'wcdocker', 'pgadmin.backform',
+  'pgadmin.backgrid'
+
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
+  codemirror, Backform, debug_function_again, AlertifyWrapper
+) {
+
+  var CodeMirror = codemirror.default;
+  if (pgAdmin.Browser.tree != null) {
+    pgAdmin = pgAdmin || window.pgAdmin || {};
+  }
+
+  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+  if (pgTools.DirectDebug)
+    return pgTools.DirectDebug;
+
+  var controller = new (function() {});
+
+  _.extend(
+    controller, Backbone.Events, {
+      enable: function(btn, enable) {
+        // trigger the event and change the button view to enable/disable the buttons for debugging
+        this.trigger('pgDebugger:button:state:' + btn, enable);
+      },
+
+      /*
+        Function to set the breakpoint and send the line no. which is set to server
+        trans_id :- Unique Transaction ID, line_no - line no. to set the breakpoint, set_type = 0 - clear , 1 - set
+      */
+      set_breakpoint: function(trans_id, line_no, set_type) {
+        var self = this;
+
+        // Make ajax call to set/clear the break point by user
+        var baseUrl = url_for('debugger.set_breakpoint', {
+                        'trans_id': trans_id,
+                        'line_no': line_no,
+                        'set_type': set_type
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              // Breakpoint has been set by the user
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while setting debugging breakpoint.'
+            );
+          }
+        });
+      },
+
+      // Function to get the latest breakpoint information and update the gutters of codemirror
+      UpdateBreakpoint: function(trans_id) {
+        var self = this;
+
+        var br_list = self.GetBreakpointInformation(trans_id);
+
+        // If there is no break point to clear then we should return from here.
+        if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+          return;
+
+        var breakpoint_list = new Array();
+
+        for (var i = 0; i < br_list.length; i++) {
+          if (br_list[i].linenumber != -1) {
+            breakpoint_list.push(br_list[i].linenumber)
+          }
+        }
+
+        for (var i = 0;i< breakpoint_list.length;i++) {
+          var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+          if (info.gutterMarkers != undefined) {
+            pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+          }
+          else {
+            pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", function() {
+            var marker = document.createElement("div");
+            marker.style.color = "#822";
+            marker.innerHTML = "●";
+            return marker;
+            }());
+          }
+        }
+      },
+
+      // Function to get the breakpoint information from the server
+      GetBreakpointInformation: function(trans_id) {
+        var self = this;
+        var result = '';
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.execute_query', {
+                        'trans_id': trans_id,
+                        'query_type': 'get_breakpoints'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          async: false,
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              result = res.data.result;
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while fetching breakpoint information.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while fetching breakpoint information.'
+            );
+          }
+        });
+
+        return result;
+      },
+
+      // Function to start the executer and execute the user requested option for debugging
+      start_execution: function(trans_id, port_num) {
+        var self = this;
+        // Make ajax call to listen the database message
+        var baseUrl = url_for(
+                        'debugger.start_execution', {
+                          'trans_id': trans_id,
+                          'port_num': port_num
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              // If status is Success then find the port number to attach the executer.
+              self.execute_query(trans_id);
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while starting debugging session.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while starting debugging session.'
+            );
+          }
+        });
+      },
+
+      // Execute the query and get the first functions debug information from the server
+      execute_query: function(trans_id) {
+        var self = this;
+        // Make ajax call to listen the database message
+        var baseUrl = url_for(
+                        'debugger.execute_query', {
+                          'trans_id': trans_id,
+                          'query_type': 'wait_for_breakpoint'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              // set the return code to the code editor text area
+              if (res.data.result[0].src != null && res.data.result[0].linenumber != null) {
+                pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+                var active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+                pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+              }
+
+              // Call function to create and update local variables ....
+              self.GetStackInformation(trans_id);
+              if (pgTools.DirectDebug.debug_type) {
+                self.poll_end_execution_result(trans_id);
+              }
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while executing requested debugging information.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while executing requested debugging information.'
+            );
+          }
+        });
+      },
+
+      // Get the local variable information of the functions and update the grid
+      GetLocalVariables: function(trans_id) {
+        var self = this;
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for(
+                        'debugger.execute_query', {
+                          'trans_id': trans_id,
+                          'query_type': 'get_variables'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              // Call function to create and update local variables
+              self.AddLocalVariables(res.data.result);
+              self.AddParameters(res.data.result);
+              // If debug function is restarted then again start listener to read the updated messages.
+              if (pgTools.DirectDebug.debug_restarted) {
+                if (pgTools.DirectDebug.debug_type) {
+                  self.poll_end_execution_result(trans_id);
+                }
+                pgTools.DirectDebug.debug_restarted = false;
+              }
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while fetching variable information.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while fetching variable information.'
+            );
+          }
+        });
+      },
+
+      // Get the stack information of the functions and update the grid
+      GetStackInformation: function(trans_id) {
+        var self = this;
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for(
+                        'debugger.execute_query', {
+                          'trans_id': trans_id,
+                          'query_type': 'get_stack_info'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              // Call function to create and update stack information
+              self.AddStackInformation(res.data.result);
+              self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while fetching stack information.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while fetching stack information.'
+            );
+          }
+        });
+      },
+
+      /*
+        poll the actual result after user has executed the "continue", "step-into", "step-over" actions and get the
+        other updated information from the server.
+      */
+      poll_result: function(trans_id) {
+      var self = this;
+
+      // Do we need to poll?
+      if(!pgTools.DirectDebug.is_polling_required){
+        return;
+      }
+
+      // Make ajax call to listen the database message
+      var baseUrl = url_for('debugger.poll_result', {'trans_id': trans_id});
+
+      /*
+        During the execution we should poll the result in minimum seconds but once the execution is completed
+        and wait for the another debugging session then we should decrease the polling frequency.
+      */
+      if (pgTools.DirectDebug.polling_timeout_idle) {
+        // poll the result after 1 second
+        var  poll_timeout = 1000;
+      }
+      else {
+        // poll the result after 200 ms
+        var  poll_timeout = 200;
+      }
+
+      setTimeout(
+        function() {
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          beforeSend: function(jqXHR, settings) {
+            // set cursor to progress before every poll.
+            $('.debugger-container').addClass('show_progress');
+          },
+          success: function(res) {
+            // remove progress cursor
+            $('.debugger-container').removeClass('show_progress');
+
+            if (res.data.status === 'Success') {
+              // If no result then poll again to wait for results.
+              if (res.data.result == null || res.data.result.length == 0) {
+                self.poll_result(trans_id);
+              }
+              else {
+                if (res.data.result[0].src != undefined || res.data.result[0].src != null) {
+                pgTools.DirectDebug.polling_timeout_idle = false;
+                pgTools.DirectDebug.docker.finishLoading(50);
+                pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+                self.UpdateBreakpoint(trans_id);
+                pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+                pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+                self.active_line_no = (res.data.result[0].linenumber - 2);
+
+                // Update the stack, local variables and parameters information
+                self.GetStackInformation(trans_id);
+
+                }
+                else if (!pgTools.DirectDebug.debug_type && !pgTools.DirectDebug.first_time_indirect_debug) {
+                  pgTools.DirectDebug.docker.finishLoading(50);
+                  if (self.active_line_no != undefined) {
+                    pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+                  }
+                  self.clear_all_breakpoint(trans_id);
+                  self.execute_query(trans_id);
+                  pgTools.DirectDebug.first_time_indirect_debug = true;
+                  pgTools.DirectDebug.polling_timeout_idle = false;
+                }
+                else {
+                  pgTools.DirectDebug.polling_timeout_idle = false;
+                  pgTools.DirectDebug.docker.finishLoading(50);
+                  // If the source is really changed then only update the breakpoint information
+                  if (res.data.result[0].src != pgTools.DirectDebug.editor.getValue()) {
+                    pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+                    self.UpdateBreakpoint(trans_id);
+                  }
+
+                  pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+                  pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+                  self.active_line_no = (res.data.result[0].linenumber - 2);
+
+                  // Update the stack, local variables and parameters information
+                  self.GetStackInformation(trans_id);
+                }
+
+                // Enable all the buttons as we got the results
+                self.enable('stop', true);
+                self.enable('step_over', true);
+                self.enable('step_into', true);
+                self.enable('continue', true);
+                self.enable('toggle_breakpoint', true);
+                self.enable('clear_all_breakpoints', true);
+              }
+            }
+            else if (res.data.status === 'Busy') {
+              pgTools.DirectDebug.polling_timeout_idle = true;
+              // If status is Busy then poll the result by recursive call to the poll function
+              if (!pgTools.DirectDebug.debug_type) {
+                pgTools.DirectDebug.docker.startLoading(gettext('Waiting for another session to invoke the target...'));
+
+                // As we are waiting for another session to invoke the target,disable all the buttons
+                self.enable('stop', false);
+                self.enable('step_over', false);
+                self.enable('step_into', false);
+                self.enable('continue', false);
+                self.enable('toggle_breakpoint', false);
+                self.enable('clear_all_breakpoints', false);
+                pgTools.DirectDebug.first_time_indirect_debug = false;
+                self.poll_result(trans_id);
+              }
+              else {
+                self.poll_result(trans_id);
+              }
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while polling result.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while polling result.'
+            );
+          }
+        });
+      }, poll_timeout );
+
+    },
+
+    // This function will update messages tab
+    update_messages: function(msg) {
+      // To prevent xss
+      msg = _.escape(msg);
+
+      var old_msgs='', new_msgs='';
+        old_msgs = pgTools.DirectDebug.messages_panel.$container.find('.messages').html();
+        if(old_msgs) {
+          new_msgs = (old_msgs + '\n' + msg)
+                        .replace(/(?:\r\n|\r|\n)/g, '<br />') // Newlines with <br>
+                        .replace(/(<br\ ?\/?>)+/g, '<br />'); // multiple <br> with single <br>
+        } else {
+          new_msgs = msg;
+        }
+        pgTools.DirectDebug.messages_panel.$container.find('.messages').html(new_msgs);
+    },
+
+    /*
+      For the direct debugging, we need to check weather the functions execution is completed or not. After completion
+      of the debugging, we will stop polling the result  until new execution starts.
+    */
+    poll_end_execution_result: function(trans_id) {
+      var self = this;
+
+      // Do we need to poll?
+      if(!pgTools.DirectDebug.is_polling_required){
+        return;
+      }
+
+      // Make ajax call to listen the database message
+      var baseUrl = url_for('debugger.poll_end_execution_result', {'trans_id': trans_id});
+
+      /*
+        During the execution we should poll the result in minimum seconds but once the execution is completed
+        and wait for the another debugging session then we should decrease the polling frequency.
+      */
+      if (pgTools.DirectDebug.polling_timeout_idle) {
+        // poll the result to check that execution is completed or not after 1200 ms
+        var  poll_end_timeout = 1200;
+      }
+      else {
+        // poll the result to check that execution is completed or not after 350 ms
+        var  poll_end_timeout = 250;
+      }
+
+      setTimeout(
+        function() {
+          $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status === 'Success') {
+              if(res.data.result == undefined ) {
+                /*
+                 "result" is undefined only in case of EDB procedure. As Once the EDB procedure execution is completed
+                 then we are not getting any result so we need ignore the result.
+                */
+                pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+                pgTools.DirectDebug.direct_execution_completed = true;
+                pgTools.DirectDebug.polling_timeout_idle = true;
+
+                //Set the alertify message to inform the user that execution is completed.
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(res.info, 3);
+
+                // Update the message tab of the debugger
+                if (res.data.status_message) {
+                  self.update_messages(res.data.status_message);
+                }
+
+                // remove progress cursor
+                $('.debugger-container').removeClass('show_progress');
+
+                // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+                // start the same execution again.
+                self.enable('stop', false);
+                self.enable('step_over', false);
+                self.enable('step_into', false);
+                self.enable('toggle_breakpoint', false);
+                self.enable('clear_all_breakpoints', false);
+                self.enable('continue', true);
+                // Stop further polling
+                pgTools.DirectDebug.is_polling_required = false;
+              }
+              else {
+                // Call function to create and update local variables ....
+                if (res.data.result != null) {
+                  pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+                  self.AddResults(res.data.col_info, res.data.result);
+                  pgTools.DirectDebug.results_panel.focus();
+                  pgTools.DirectDebug.direct_execution_completed = true;
+                  pgTools.DirectDebug.polling_timeout_idle = true;
+
+                  //Set the alertify message to inform the user that execution is completed.
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.success(res.info, 3);
+
+                  // Update the message tab of the debugger
+                  if (res.data.status_message) {
+                    self.update_messages(res.data.status_message);
+                  }
+
+                  // remove progress cursor
+                  $('.debugger-container').removeClass('show_progress');
+
+                  // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+                  // start the same execution again.
+                  self.enable('stop', false);
+                  self.enable('step_over', false);
+                  self.enable('step_into', false);
+                  self.enable('toggle_breakpoint', false);
+                  self.enable('clear_all_breakpoints', false);
+                  self.enable('continue', true);
+
+                  // Stop further pooling
+                  pgTools.DirectDebug.is_polling_required = false;
+                }
+              }
+            }
+            else if (res.data.status === 'Busy') {
+              // If status is Busy then poll the result by recursive call to the poll function
+              self.poll_end_execution_result(trans_id);
+              // Update the message tab of the debugger
+              if (res.data.status_message) {
+                self.update_messages(res.data.status_message);
+              }
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger poll end execution error',
+                res.data.result
+              );
+            }
+            else if (res.data.status === 'ERROR') {
+              pgTools.DirectDebug.direct_execution_completed = true;
+              pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+
+              //Set the alertify message to inform the user that execution is completed with error.
+              var alertifyWrapper = new AlertifyWrapper();
+
+              if(!pgTools.DirectDebug.is_user_aborted_debugging) {
+                alertifyWrapper.error(res.info, 3);
+              }
+
+              // Update the message tab of the debugger
+              if (res.data.status_message) {
+                self.update_messages(res.data.status_message);
+              }
+
+              pgTools.DirectDebug.messages_panel.focus();
+
+              // remove progress cursor
+              $('.debugger-container').removeClass('show_progress');
+
+              // Execution completed so disable the buttons other than
+              // "Continue/Start" button because user can still start the
+              // same execution again.
+              self.enable('stop', false);
+              self.enable('step_over', false);
+              self.enable('step_into', false);
+              self.enable('toggle_breakpoint', false);
+              self.enable('clear_all_breakpoints', false);
+              // If debugging is stopped by user then do not enable
+              // continue/restart button
+              if(!pgTools.DirectDebug.is_user_aborted_debugging)
+              {
+                self.enable('continue', true);
+                pgTools.DirectDebug.is_user_aborted_debugging = false;
+              }
+
+              // Stop further pooling
+              pgTools.DirectDebug.is_polling_required = false;
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while polling result.'
+            );
+          }
+        });
+      }, poll_end_timeout);
+
+    },
+
+    Restart: function(trans_id) {
+
+      var self = this,
+        baseUrl = url_for('debugger.restart', {'trans_id': trans_id});
+      self.enable('stop', false);
+      self.enable('step_over', false);
+      self.enable('step_into', false);
+      self.enable('toggle_breakpoint', false);
+      self.enable('clear_all_breakpoints', false);
+      self.enable('continue', false);
+
+      // Clear msg tab
+      pgTools.DirectDebug.messages_panel.$container.find('.messages').html('');
+
+      $.ajax({
+        url: baseUrl,
+        success: function(res) {
+          // Restart the same function debugging with previous arguments
+          var restart_dbg = res.data.restart_debug ? 1 : 0;
+
+          // Start pooling again
+          pgTools.DirectDebug.polling_timeout_idle = false;
+          pgTools.DirectDebug.is_polling_required = true;
+          self.poll_end_execution_result(trans_id);
+          self.poll_result(trans_id);
+
+          if (restart_dbg) {
+            pgTools.DirectDebug.debug_restarted = true;
+          }
+
+          /*
+           Need to check if restart debugging really require to open the input dialog ?
+           If yes then we will get the previous arguments from database and populate the input dialog
+           If no then we should directly start the listener.
+          */
+          if (res.data.result.require_input) {
+            var res_val = debug_function_again(res.data.result, restart_dbg);
+          }
+          else {
+            // Debugging of void function is started again so we need to start the listener again
+            var baseUrl = url_for('debugger.start_listener', {'trans_id': trans_id});
+
+            $.ajax({
+              url: baseUrl,
+              method: 'GET',
+              success: function(res) {
+                if (pgTools.DirectDebug.debug_type) {
+                  self.poll_end_execution_result(trans_id);
+                }
+              },
+              error: function(e) {
+                Alertify.alert(
+                  'Debugger Error',
+                  'Error while polling result.'
+                );
+              }
+            });
+          }
+        },
+        error: function(xhr, status, error) {
+          try {
+            var err = $.parseJSON(xhr.responseText);
+            if (err.success == 0) {
+              Alertify.alert(err.errormsg);
+            }
+          } catch (e) {}
+        }
+      });
+    },
+
+    // Continue the execution until the next breakpoint
+    Continue: function(trans_id) {
+      var self = this;
+      self.enable('stop', false);
+      self.enable('step_over', false);
+      self.enable('step_into', false);
+      self.enable('toggle_breakpoint', false);
+      self.enable('clear_all_breakpoints', false);
+      self.enable('continue', false);
+
+      //Check first if previous execution was completed or not
+      if (pgTools.DirectDebug.direct_execution_completed &&
+          pgTools.DirectDebug.direct_execution_completed == pgTools.DirectDebug.polling_timeout_idle) {
+        self.Restart(trans_id);
+      }
+      else {
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.execute_query', {
+                        'trans_id': trans_id,
+                        'query_type': 'continue'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              self.poll_result(trans_id);
+            }
+            else {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while executing continue in debugging session.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while executing continue in debugging session.'
+            );
+          }
+        });
+      }
+    },
+
+      Step_over: function(trans_id) {
+        var self = this;
+        self.enable('stop', false);
+        self.enable('step_over', false);
+        self.enable('step_into', false);
+        self.enable('toggle_breakpoint', false);
+        self.enable('clear_all_breakpoints', false);
+        self.enable('continue', false);
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.execute_query', {
+                        'trans_id': trans_id,
+                        'query_type': 'step_over'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              self.poll_result(trans_id);
+            }
+            else {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while executing step over in debugging session.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while executing step over in debugging session.'
+            );
+          }
+        });
+      },
+
+      Step_into: function(trans_id) {
+        var self = this;
+        self.enable('stop', false);
+        self.enable('step_over', false);
+        self.enable('step_into', false);
+        self.enable('toggle_breakpoint', false);
+        self.enable('clear_all_breakpoints', false);
+        self.enable('continue', false);
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.execute_query', {
+                        'trans_id': trans_id,
+                        'query_type': 'step_into'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              self.poll_result(trans_id);
+            }
+            else {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while executing step into in debugging session.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while executing step into in debugging session.'
+            );
+          }
+        });
+      },
+
+      Stop: function(trans_id) {
+        var self = this;
+        self.enable('stop', false);
+        self.enable('step_over', false);
+        self.enable('step_into', false);
+        self.enable('toggle_breakpoint', false);
+        self.enable('clear_all_breakpoints', false);
+        self.enable('continue', false);
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for(
+                        'debugger.execute_query', {
+                          'trans_id': trans_id,
+                          'query_type': 'abort_target'
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              // Call function to create and update local variables ....
+              pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+              pgTools.DirectDebug.direct_execution_completed = true;
+              pgTools.DirectDebug.is_user_aborted_debugging = true;
+
+              // Stop further pooling
+              pgTools.DirectDebug.is_polling_required = false;
+
+              // Restarting debugging in the same transaction do not work
+              // We will give same behaviour as pgAdmin3 and disable all buttons
+              self.enable('continue', false);
+
+              // Set the alertify message to inform the user that execution
+              // is completed.
+              var alertifyWrapper = new AlertifyWrapper();
+              alertifyWrapper.success(res.info, 3);
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while executing stop in debugging session.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while executing stop in debugging session.'
+            );
+          }
+        });
+      },
+
+      toggle_breakpoint: function(trans_id) {
+        var self = this;
+        self.enable('stop', false);
+        self.enable('step_over', false);
+        self.enable('step_into', false);
+        self.enable('toggle_breakpoint', false);
+        self.enable('clear_all_breakpoints', false);
+        self.enable('continue', false);
+
+
+        var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+        var baseUrl = '';
+
+        // If gutterMarker is undefined that means there is no marker defined previously
+        // So we need to set the breakpoint command here...
+        if (info.gutterMarkers == undefined) {
+            baseUrl = url_for('debugger.set_breakpoint', {
+                        'trans_id': trans_id,
+                        'line_no': self.active_line_no + 1,
+                        'set_type': '1'
+                      });
+        }
+        else {
+            baseUrl = url_for('debugger.set_breakpoint', {
+                        'trans_id': trans_id,
+                        'line_no': self.active_line_no + 1,
+                        'set_type': '0'
+                      });
+        }
+
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              // Call function to create and update local variables ....
+              var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+
+              if (info.gutterMarkers != undefined) {
+                pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", null);
+              }
+              else {
+                pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", function() {
+                    var marker = document.createElement("div");
+                    marker.style.color = "#822";
+                    marker.innerHTML = "●";
+                    return marker;
+                }());
+              }
+              self.enable('stop', true);
+              self.enable('step_over', true);
+              self.enable('step_into', true);
+              self.enable('toggle_breakpoint', true);
+              self.enable('clear_all_breakpoints', true);
+              self.enable('continue', true);
+            }
+            else if (res.data.status === 'NotConnected') {
+              Alertify.alert(
+                'Debugger Error',
+                'Error while toggling breakpoint.'
+              );
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while toggling breakpoint.'
+            );
+          }
+        });
+      },
+
+      clear_all_breakpoint: function(trans_id) {
+        var self = this,
+          br_list = self.GetBreakpointInformation(trans_id);
+
+        // If there is no break point to clear then we should return from here.
+        if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+          return;
+
+        self.enable('stop', false);
+        self.enable('step_over', false);
+        self.enable('step_into', false);
+        self.enable('toggle_breakpoint', false);
+        self.enable('clear_all_breakpoints', false);
+        self.enable('continue', false);
+
+        var breakpoint_list = new Array();
+
+        for (var i = 0; i < br_list.length; i++) {
+          if (br_list[i].linenumber != -1) {
+            breakpoint_list.push(br_list[i].linenumber)
+          }
+        }
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.clear_all_breakpoint', {'trans_id': trans_id});
+
+        $.ajax({
+          url: baseUrl,
+          method: 'POST',
+          data: { 'breakpoint_list': breakpoint_list.join() },
+          success: function(res) {
+            if (res.data.status) {
+              for (var i = 0; i < breakpoint_list.length; i++) {
+                var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+                if (info) {
+                  if (info.gutterMarkers != undefined) {
+                    pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+                  }
+                }
+              }
+            }
+          self.enable('stop', true);
+          self.enable('step_over', true);
+          self.enable('step_into', true);
+          self.enable('toggle_breakpoint', true);
+          self.enable('clear_all_breakpoints', true);
+          self.enable('continue', true);
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while clearing all breakpoint.'
+            );
+          }
+        });
+      },
+
+      AddStackInformation: function(result) {
+        var self = this;
+
+        // Remove the existing created grid and update the stack values
+        if (self.stack_grid) {
+            self.stack_grid.remove();
+            self.stack_grid = null;
+        }
+
+        var DebuggerStackModel = Backbone.Model.extend({
+          defaults: {
+            name: undefined,
+            value: undefined,
+            line_no: undefined
+          }
+        });
+
+        // Collection which contains the model for function informations.
+        var StackCollection = Backbone.Collection.extend({
+          model: DebuggerStackModel
+        });
+
+        var stackGridCols = [
+          {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+          {name: 'value', label:'Value', type:'text', editable: false, cell:'string'},
+          {name: 'line_no', label:'Line No.', type:'text', editable: false, cell:'string'}
+        ];
+
+        var my_obj = [];
+        if (result.length != 0)
+        {
+          for (var i = 0; i < result.length; i++) {
+            my_obj.push({ "name": result[i].targetname, "value": result[i].args, "line_no": result[i].linenumber });
+          }
+        }
+
+        var stackColl = this.stackColl = new StackCollection(my_obj);
+        this.stackColl.on('backgrid:row:selected', self.select_frame, self);
+
+        // Initialize a new Grid instance
+        var stack_grid = this.stack_grid = new Backgrid.Grid({
+          columns: stackGridCols,
+          row: Backgrid.Row.extend({
+            highlightColor: "#D9EDF7",
+            disabledColor: "#F1F1F1",
+            events: {
+              click: "rowClick"
+            },
+            rowClick: function(e) {
+              //Find which row is selected and depending on that send the frame id
+              for (var i = 0; i < this.model.collection.length; i++) {
+                if (this.model.collection.models[i].get('name') == this.model.get('name')) {
+                  self.frame_id_ = i;
+                  break;
+                }
+              }
+              this.model.trigger('backgrid:row:selected', this);
+              self.stack_grid.$el.find("td").css("background-color", this.disabledColor);
+              this.$el.find("td").css("background-color", this.highlightColor);
+            }
+          }),
+          collection: stackColl,
+          className: "backgrid table-bordered"
+        });
+
+        stack_grid.render();
+
+        // Render the stack grid into stack panel
+        pgTools.DirectDebug.stack_pane_panel.$container.find('.stack_pane').append(stack_grid.el);
+
+      },
+
+      AddResults: function(columns, result) {
+        var self = this;
+
+        // Remove the existing created grid and update the result values
+        if (self.result_grid) {
+            self.result_grid.remove();
+            self.result_grid = null;
+        }
+
+        var DebuggerResultsModel = Backbone.Model.extend({
+          defaults: {
+            name: undefined
+          }
+        });
+
+        // Collection which contains the model for function informations.
+        var ResultsCollection = Backbone.Collection.extend({
+          model: DebuggerResultsModel
+        });
+
+        var resultGridCols = [];
+        if(_.size(columns)) {
+          _.each(columns, function(c) {
+            var column = {
+                            type:'text',
+                            editable: false,
+                            cell:'string'
+                         };
+            column['name'] = column['label'] = c.name;
+            resultGridCols.push(column);
+          });
+        }
+
+        // Initialize a new Grid instance
+        var result_grid = this.result_grid = new Backgrid.Grid({
+          columns: resultGridCols,
+          collection: new ResultsCollection(result),
+          className: "backgrid table-bordered"
+        });
+
+        result_grid.render();
+
+        // Render the result grid into result panel
+        pgTools.DirectDebug.results_panel.$container.find('.debug_results').append(result_grid.el);
+
+      },
+
+      AddLocalVariables: function(result) {
+        var self = this;
+
+        // Remove the existing created grid and update the variables values
+        if (self.variable_grid) {
+            self.variable_grid.remove();
+            self.variable_grid = null;
+        }
+
+        var DebuggerVariablesModel = Backbone.Model.extend({
+          defaults: {
+            name: undefined,
+            type: undefined,
+            value: undefined
+          }
+        });
+
+        // Collection which contains the model for function informations.
+        var VariablesCollection = Backbone.Collection.extend({
+          model: DebuggerVariablesModel
+        });
+
+        var gridCols = [
+          {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+          {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+          {name: 'value', label:'Value', type: 'text', cell: 'string'}
+        ];
+
+        var my_obj = [];
+        if (result.length != 0)
+        {
+          for (var i = 0; i < result.length; i++) {
+            if (result[i].varclass == 'L') {
+              my_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+            }
+          }
+        }
+
+        // Initialize a new Grid instance
+        var variable_grid = this.variable_grid = new Backgrid.Grid({
+          columns: gridCols,
+          collection: new VariablesCollection(my_obj),
+          className: "backgrid table-bordered"
+        });
+
+        variable_grid.render();
+
+        // Render the variables grid into local variables panel
+        pgTools.DirectDebug.local_variables_panel.$container.find('.local_variables').append(variable_grid.el);
+
+      },
+
+      AddParameters: function(result) {
+        var self = this;
+
+        // Remove the existing created grid and update the parameter values
+        if (self.param_grid) {
+            self.param_grid.remove();
+            self.param_grid = null;
+        }
+
+        var DebuggerParametersModel = Backbone.Model.extend({
+          defaults: {
+            name: undefined,
+            type: undefined,
+            value: undefined
+          }
+        });
+
+        // Collection which contains the model for function informations.
+        var ParametersCollection = self.ParametersCollection = Backbone.Collection.extend({
+          model: DebuggerParametersModel
+        });
+
+        self.ParametersCollection.prototype.on('change', self.deposit_parameter_value, self);
+
+        var paramGridCols = [
+          {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+          {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+          {name: 'value', label:'Value', type: 'text', cell: 'string'}
+        ];
+
+        var param_obj = [];
+        if (result.length != 0)
+        {
+          for (var i = 0; i < result.length; i++) {
+            if (result[i].varclass == 'A') {
+              param_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+            }
+          }
+        }
+
+        // Initialize a new Grid instance
+        var param_grid = this.param_grid = new Backgrid.Grid({
+          columns: paramGridCols,
+          collection: new ParametersCollection(param_obj),
+          className: "backgrid table-bordered"
+        });
+
+        param_grid.render();
+
+        // Render the parameters grid into parameter panel
+        pgTools.DirectDebug.parameters_panel.$container.find('.parameters').append(param_grid.el);
+      },
+
+      deposit_parameter_value: function(model) {
+        var self = this;
+
+        // variable name and value list that is changed by user
+        var name_value_list = [];
+
+        name_value_list.push({ 'name': model.get('name'),'type': model.get('type'), 'value': model.get('value')});
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.deposit_value', {
+                        'trans_id': pgTools.DirectDebug.trans_id
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'POST',
+          data:{'data':JSON.stringify(name_value_list)},
+          success: function(res) {
+            if (res.data.status) {
+              // Get the updated variables value
+              self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+              // Show the message to the user that deposit value is success or failure
+              var alertifyWrapper = new AlertifyWrapper();
+              if (res.data.result) {
+                alertifyWrapper.success(res.data.info, 3);
+              } else {
+                alertifyWrapper.error(res.data.info, 3);
+              }
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while depositing variable value.'
+            );
+          }
+        });
+      },
+
+      select_frame: function(model, selected) {
+        var self = this;
+
+        // Make ajax call to listen the database message
+        var baseUrl = url_for('debugger.select_frame', {
+                        'trans_id': pgTools.DirectDebug.trans_id,
+                        'frame_id': self.frame_id_
+                      });
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+              self.UpdateBreakpoint(pgTools.DirectDebug.trans_id);
+              //active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+              pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+
+              // Call function to create and update local variables ....
+              self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while selecting frame.'
+            );
+          }
+        });
+      },
+    }
+  )
+
+  /*
+    Debugger tool var view to create the button toolbar and listen to the button click event and inform the
+    controller about the click and controller will take the action for the specified button click.
+  */
+  var DebuggerToolbarView = Backbone.View.extend({
+    el: '#btn-toolbar',
+    initialize: function() {
+      controller.on('pgDebugger:button:state:stop', this.enable_stop, this);
+      controller.on('pgDebugger:button:state:step_over', this.enable_step_over, this);
+      controller.on('pgDebugger:button:state:step_into', this.enable_step_into, this);
+      controller.on('pgDebugger:button:state:continue', this.enable_continue, this);
+      controller.on('pgDebugger:button:state:toggle_breakpoint', this.enable_toggle_breakpoint, this);
+      controller.on('pgDebugger:button:state:clear_all_breakpoints', this.enable_clear_all_breakpoints, this);
+    },
+    events: {
+        'click .btn-stop': 'on_stop',
+        'click .btn-clear-breakpoint': 'on_clear_all_breakpoint',
+        'click .btn-toggle-breakpoint': 'on_toggle_breakpoint',
+        'click .btn-continue': 'on_continue',
+        'click .btn-step-over': 'on_step_over',
+        'click .btn-step-into': 'on_step_into'
+    },
+    enable_stop: function(enable) {
+      var $btn = this.$el.find('.btn-stop');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+    enable_step_over: function(enable) {
+      var $btn = this.$el.find('.btn-step-over');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+    enable_step_into: function(enable) {
+      var $btn = this.$el.find('.btn-step-into');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+    enable_continue: function(enable) {
+      var $btn = this.$el.find('.btn-continue');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+    enable_toggle_breakpoint: function(enable) {
+      var $btn = this.$el.find('.btn-toggle-breakpoint');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+    enable_clear_all_breakpoints: function(enable) {
+      var $btn = this.$el.find('.btn-clear-breakpoint');
+
+      if (enable) {
+        $btn.prop('disabled', false);
+        $btn.removeAttr('disabled');
+      } else {
+        $btn.prop('disabled', true);
+        $btn.attr('disabled', 'disabled');
+      }
+    },
+
+    on_stop: function() {
+      controller.Stop(pgTools.DirectDebug.trans_id);
+    },
+    on_clear_all_breakpoint: function() {
+      controller.clear_all_breakpoint(pgTools.DirectDebug.trans_id);
+    },
+    on_toggle_breakpoint: function() {
+      controller.toggle_breakpoint(pgTools.DirectDebug.trans_id);
+    },
+    on_continue: function() {
+      controller.Continue(pgTools.DirectDebug.trans_id);
+    },
+    on_step_over: function() {
+      controller.Step_over(pgTools.DirectDebug.trans_id);
+    },
+    on_step_into: function() {
+      controller.Step_into(pgTools.DirectDebug.trans_id);
+    },
+  });
+
+
+  /*
+    Function is responsible to create the new wcDocker instance for debugger and initialize the debugger panel inside
+    the docker instance.
+  */
+  var DirectDebug = function() {};
+
+  _.extend(DirectDebug.prototype, {
+    init: function(trans_id, debug_type) { /* We should get the transaction id from the server during initialization here */
+      // We do not want to initialize the module multiple times.
+
+      var self = this;
+      _.bindAll(pgTools.DirectDebug, 'messages');
+
+      if (this.initialized)
+          return;
+
+      this.initialized = true;
+      this.trans_id = trans_id;
+      this.debug_type = debug_type;
+      this.first_time_indirect_debug = false;
+      this.direct_execution_completed = false;
+      this.polling_timeout_idle = false;
+      this.debug_restarted = false;
+      this.is_user_aborted_debugging = false;
+      this.is_polling_required = true; // Flag to stop unwanted ajax calls
+
+      var docker = this.docker = new wcDocker(
+          '#container', {
+          allowContextMenu: false,
+          allowCollapse: false,
+          themePath: url_for('static', {'filename': 'css'}),
+          theme: 'webcabin.overrides.css'
+        });
+
+      this.panels = [];
+
+      // Below code will be executed for indirect debugging
+      // indirect debugging - 0  and for direct debugging - 1
+      if (trans_id != undefined && !debug_type) {
+        // Make ajax call to execute the and start the target for execution
+        var baseUrl = url_for('debugger.start_listener', {'trans_id': trans_id });
+
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              self.intializePanels();
+              controller.poll_result(trans_id);
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while starting debugging listener.'
+            );
+          }
+        });
+      }
+      else if (trans_id != undefined && debug_type)
+      {
+        // Make ajax call to execute the and start the target for execution
+        var baseUrl = url_for('debugger.start_listener', {'trans_id': trans_id });
+
+        $.ajax({
+          url: baseUrl,
+          method: 'GET',
+          success: function(res) {
+            if (res.data.status) {
+              self.messages(trans_id);
+            }
+          },
+          error: function(e) {
+            Alertify.alert(
+              'Debugger Error',
+              'Error while starting debugging listener.'
+            );
+          }
+        });
+      }
+      else
+        this.intializePanels();
+    },
+
+    // Read the messages of the database server and get the port ID and attach the executer to that port.
+    messages: function(trans_id) {
+      var self = this;
+      // Make ajax call to listen the database message
+      var baseUrl = url_for('debugger.messages', {'trans_id': trans_id });
+
+      $.ajax({
+        url: baseUrl,
+        method: 'GET',
+        success: function(res) {
+          if (res.data.status === 'Success') {
+            self.intializePanels();
+            // If status is Success then find the port number to attach the executer.
+            //self.start_execution(trans_id, res.data.result);
+            controller.start_execution(trans_id, res.data.result);
+          }
+          else if (res.data.status === 'Busy') {
+            // If status is Busy then poll the result by recursive call to the poll function
+            self.messages(trans_id);
+          }
+          else if (res.data.status === 'NotConnected') {
+            Alertify.alert(
+              'Not connected to server or connection with the server has been closed.',
+              res.data.result
+            );
+          }
+        },
+        error: function(e) {
+          Alertify.alert(
+            'Debugger Error',
+            'Error while fetching messages information.'
+          );
+        }
+      });
+
+    },
+
+    // Callback function when user click on gutters of codemirror to set/clear the breakpoint
+    onBreakPoint: function(cm, m, gutter) {
+      var self = this;
+
+      // If breakpoint gutter is clicked and execution is not completed then only set the breakpoint
+      if (gutter == "breakpoints" && !pgTools.DirectDebug.polling_timeout_idle ) {
+        // We may want to check, if break-point is allowed at this moment or not
+        var info = cm.lineInfo(m);
+
+        // If gutterMarker is undefined that means there is no marker defined previously
+        // So we need to set the breakpoint command here...
+        if (info.gutterMarkers == undefined) {
+          controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
+        }
+        else {
+          if (info.gutterMarkers.breakpoints == undefined) {
+            controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
+          }
+          else {
+            controller.set_breakpoint(self.trans_id,m+1,0); //clear the breakpoint
+          }
+        }
+
+        // If line folding is defined then gutterMarker will be defined so
+        // we need to find out 'breakpoints' information
+        var markers = info.gutterMarkers;
+        if (markers != undefined && info.gutterMarkers.breakpoints == undefined)
+          markers = info.gutterMarkers.breakpoints
+
+        cm.setGutterMarker(
+          m, "breakpoints", markers ? null : function() {
+            var marker = document.createElement("div");
+
+            marker.style.color = "#822";
+            marker.innerHTML = "●";
+
+            return marker;
+        }());
+      }
+    },
+
+    // Create the debugger layout with splitter and display the appropriate data received from server.
+    intializePanels: function() {
+      var self = this;
+      this.registerPanel(
+        'code', false, '100%', '50%',
+        function(panel) {
+
+            // Create the parameters panel to display the arguments of the functions
+            var parameters = new pgAdmin.Browser.Panel({
+              name: 'parameters',
+              title: gettext('Parameters'),
+              width: '100%',
+              height:'100%',
+              isCloseable: false,
+              isPrivate: true,
+              content: '<div id ="parameters" class="parameters"></div>'
+            })
+
+            // Create the Local variables panel to display the local variables of the function.
+            var local_variables = new pgAdmin.Browser.Panel({
+              name: 'local_variables',
+              title: gettext('Local variables'),
+              width: '100%',
+              height:'100%',
+              isCloseable: false,
+              isPrivate: true,
+              content: '<div id ="local_variables" class="local_variables"></div>'
+            })
+
+            // Create the messages panel to display the message returned from the database server
+            var messages = new pgAdmin.Browser.Panel({
+              name: 'messages',
+              title: gettext('Messages'),
+              width: '100%',
+              height:'100%',
+              isCloseable: false,
+              isPrivate: true,
+              content: '<div id="messages" class="messages"></div>'
+            })
+
+            // Create the result panel to display the result after debugging the function
+            var results = new pgAdmin.Browser.Panel({
+              name: 'results',
+              title: gettext('Results'),
+              width: '100%',
+              height:'100%',
+              isCloseable: false,
+              isPrivate: true,
+              content: '<div id="debug_results" class="debug_results"></div>'
+            })
+
+            // Create the stack pane panel to display the debugging stack information.
+            var stack_pane = new pgAdmin.Browser.Panel({
+              name: 'stack_pane',
+              title: gettext('Stack'),
+              width: '100%',
+              height:'100%',
+              isCloseable: false,
+              isPrivate: true,
+              content: '<div id="stack_pane" class="stack_pane"></div>'
+            })
+
+            // Load all the created panels
+            parameters.load(self.docker);
+            local_variables.load(self.docker);
+            messages.load(self.docker);
+            results.load(self.docker);
+            stack_pane.load(self.docker);
+        });
+
+        self.code_editor_panel = self.docker.addPanel('code', wcDocker.DOCK.TOP );
+
+        self.parameters_panel = self.docker.addPanel(
+          'parameters', wcDocker.DOCK.BOTTOM, self.code_editor_panel);
+        self.local_variables_panel = self.docker.addPanel('local_variables', wcDocker.DOCK.STACKED, self.parameters_panel, {
+          tabOrientation: wcDocker.TAB.TOP
+        });
+        self.messages_panel = self.docker.addPanel('messages', wcDocker.DOCK.STACKED, self.parameters_panel);
+        self.results_panel = self.docker.addPanel(
+          'results', wcDocker.DOCK.STACKED, self.parameters_panel);
+        self.stack_pane_panel = self.docker.addPanel(
+          'stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
+
+        var editor_pane = $('<div id="stack_editor_pane" class="full-container-pane info"></div>');
+        var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
+        self.code_editor_panel.layout().addItem(code_editor_area);
+
+        // To show the line-number and set breakpoint marker details by user.
+        var editor = self.editor = CodeMirror.fromTextArea(
+          code_editor_area.get(0), {
+          lineNumbers: true,
+          foldOptions: {
+            widget: "\u2026"
+          },
+          foldGutter: {
+            rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
+                                                 CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
+          },
+          gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
+          mode: "text/x-pgsql",
+          readOnly: true,
+          extraKeys: pgAdmin.Browser.editor_shortcut_keys,
+          tabSize: pgAdmin.Browser.editor_options.tabSize,
+          lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
+          autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
+          matchBrackets: pgAdmin.Browser.editor_options.brace_matching
+        });
+
+        // On loading the docker, register the callbacks
+        var onLoad = function() {
+          self.docker.finishLoading(100);
+          self.docker.off(wcDocker.EVENT.LOADED);
+          // Register the callback when user set/clear the breakpoint on gutter area.
+          self.editor.on("gutterClick", self.onBreakPoint.bind(self), self);
+        };
+
+        self.docker.startLoading(gettext('Loading...'));
+        self.docker.on(wcDocker.EVENT.LOADED, onLoad);
+
+        // Create the toolbar view for debugging the function
+        this.toolbarView = new DebuggerToolbarView();
+    },
+
+    // Register the panel with new debugger docker instance.
+    registerPanel: function(name, title, width, height, onInit) {
+      var self = this;
+
+      this.docker.registerPanelType(name, {
+        title: title,
+        isPrivate: true,
+        onCreate: function(panel) {
+          self.panels[name] = panel;
+          panel.initSize(width, height);
+          if (!title)
+            panel.title(false);
+          else
+            panel.title(title);
+          panel.closeable(false);
+          panel.layout().addItem(
+            $('<div>', {'class': 'pg-debugger-panel'})
+          );
+          if (onInit) {
+            onInit.apply(self, [panel]);
+          }
+        }
+      });
+    }
+  });
+
+  pgTools.DirectDebug = new DirectDebug();
+  pgTools.DirectDebug['jquery'] = $;
+
+  return pgTools.DirectDebug;
+});
diff --git a/web/pgadmin/tools/grant_wizard/__init__.py b/web/pgadmin/tools/grant_wizard/__init__.py
index 488a7e22..44c9b421 100644
--- a/web/pgadmin/tools/grant_wizard/__init__.py
+++ b/web/pgadmin/tools/grant_wizard/__init__.py
@@ -9,6 +9,7 @@
 
 """Implements Grant Wizard"""
 
+import os
 import simplejson as json
 from flask import Response, url_for
 from flask import render_template, request, current_app
@@ -65,13 +66,9 @@ class GrantWizardModule(PgAdminModule):
         scripts = []
         scripts.append({
             'name': 'pgadmin.tools.grant_wizard',
-            'path': url_for('grant_wizard.index') + 'grant_wizard',
-            'when': None
-        })
-        scripts.append({
-            'name': 'pgadmin.browser.wizard',
-            'path': url_for('browser.static', filename='js/wizard'),
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/grant_wizard'),
+            'when': None,
+            'is_template': False
         })
         return scripts
 
@@ -103,7 +100,8 @@ class GrantWizardModule(PgAdminModule):
 
 # Create blueprint for GrantWizardModule class
 blueprint = GrantWizardModule(
-    MODULE_NAME, __name__, static_url_path='')
+    MODULE_NAME, __name__, static_url_path=''
+)
 
 
 def check_precondition(f):
diff --git a/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
new file mode 100644
index 00000000..3de742a7
--- /dev/null
+++ b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
@@ -0,0 +1,1129 @@
+// Grant Wizard
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'alertify', 'pgadmin.browser', 'backbone', 'backgrid',
+  'pgadmin.browser.node', 'backgrid.select.all', 'backgrid.filter',
+  'pgadmin.browser.server.privilege', 'pgadmin.browser.wizard',
+], function(gettext, url_for, $, _, S, alertify, pgBrowser, Backbone, Backgrid, pgNode) {
+
+    // if module is already initialized, refer to that.
+    if (pgBrowser.GrantWizard) {
+      return pgBrowser.GrantWizard;
+    }
+
+    /**
+      It is sub model for field "Objects". It has fields
+      for database object types such as Schemas, Views and
+      Sequence etc.
+    */
+    var DatabaseObjectModel = pgNode.Model.extend({
+      defaults: {
+        selected: false,
+        icon: 'icon-unknown',
+        name: undefined,
+        name_with_args: undefined,
+        nspname: undefined,
+        proargs: undefined,
+        object_type: undefined,
+        object_id: undefined
+      },
+      idAttribute: 'object_id', // to uniquely identify a model object
+      toJSON: function(obj) {
+        var d = pgNode.Model.prototype.toJSON.apply(this);
+        delete d.icon;
+        return d;
+      },
+      parse: function(res) {
+
+        // Create unique object id
+        res.object_id = res.name_with_args;
+
+        // create name with args if its object is function
+        if(!_.isUndefined(res.object_type) &&
+          (res.object_type == 'Function' ||
+          res.object_type == 'Trigger Function' ||
+          res.object_type == 'Procedure'
+          ))
+          res.name_with_args = res.name+'('+(typeof(res.proargs) != 'undefined' ? res.proargs : '')+')';
+        else
+          res.name_with_args = res.name;
+
+        return res;
+      },
+
+      validate: function() {
+
+        /*
+        * Triggers error messages for object types "selected"
+        * if it is empty/undefined/null
+        */
+        var err = {},
+          errmsg,
+          node = this.get('objects').toJSON();
+        if (_.isEmpty(node)) {
+          err['selected'] = gettext('Please select any database object type.');
+          errmsg = errmsg || err['selected'];
+          this.errorModel.set('selected', errmsg);
+          return errmsg;
+        } else {
+          this.errorModel.unset('selected');
+        }
+        return null;
+      }
+    });
+
+    // Define columns for the Db Object Types grid
+    var columns = [{
+      name: "selected",
+
+      /*
+      Override render method of Backgrid.Extension.SelectRowCell
+      class. It has an issue: It doesn't mark rows checked if we move to next
+      page and then go back to previous page. but it must show.
+      so we handle this case by overriding the render method.
+      */
+      cell: Backgrid.Extension.SelectRowCell.extend({
+        render: function() {
+
+          // Use the Backform Control's render function
+          Backgrid.Extension.SelectRowCell.prototype.render.apply(this, arguments);
+
+          var col = this.column.get('name');
+          if (this.model && this.model.has(col)) {
+            if (this.model.get(col)) {
+              this.$el.parent().toggleClass("selected", true);
+              this.model.trigger("backgrid:selected", this.model, true);
+            }
+          }
+          return this;
+        }
+      }),
+
+      headerCell: "select-all",
+
+    },{
+      name: "object_type",
+      label: "Object Type",
+      editable: false,
+      cell: Backgrid.Cell.extend({
+        render: function() {
+
+          // Override render to add icon to Db Object column
+          Backgrid.Cell.prototype.render.apply(this, arguments);
+          this.$el.addClass(this.model.get('icon')).css({"padding-left": "24px"});
+
+          return this;
+        }
+      })
+    },{
+      name: "nspname",
+      label: "Schema",
+      cell: "string",
+      editable: false
+    },{
+      name: "name_with_args",
+      label: "Name",
+      cell: "string",
+      editable: false
+    }];
+
+    // Create an Object GrantWizard of pgBrowser class
+    pgBrowser.GrantWizard  = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        // Define list of nodes on which grant wizard context menu option appears
+        var supported_nodes = [
+              'schema', 'coll-function', 'coll-sequence',
+              'coll-table', 'coll-view', 'coll-procedure',
+              'coll-mview', 'database', 'coll-trigger_function'
+            ],
+
+            /**
+              Enable/disable grantwizard menu in tools based
+              on node selected
+              if selected node is present in supported_nodes,
+              menu will be enabled otherwise disabled.
+              Also, hide it for system view in catalogs
+            */
+            menu_enabled = function(itemData, item, data) {
+              var t = pgBrowser.tree, i = item, d = itemData;
+              var parent_item = t.hasParent(i) ? t.parent(i): null,
+                  parent_data = parent_item ? t.itemData(parent_item) : null;
+              if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
+                if (_.indexOf(supported_nodes, d._type) !== -1 &&
+                  parent_data._type != 'catalog') {
+                    if (d._type == 'database' && d.allowConn)
+                      return true;
+                    else if(d._type != 'database')
+                      return true;
+                    else
+                      return false;
+                }
+                else
+                  return false;
+              }
+              else
+                return false;
+            };
+
+        // Define the nodes on which the menus to be appear
+        var menus = [{
+          name: 'grant_wizard_schema', module: this,
+          applies: ['tools'], callback: 'start_grant_wizard',
+          priority: 14, label: gettext('Grant Wizard...'),
+          icon: 'fa fa-unlock-alt', enable: menu_enabled
+        }];
+
+        // Add supported menus into the menus list
+        for (var idx = 0; idx < supported_nodes.length; idx++) {
+          menus.push({
+            name: 'grant_wizard_schema_context_' + supported_nodes[idx],
+            node: supported_nodes[idx], module: this,
+            applies: ['context'], callback: 'start_grant_wizard',
+            priority: 14, label: gettext('Grant Wizard...'),
+            icon: 'fa fa-unlock-alt', enable: menu_enabled
+            });
+        }
+        pgAdmin.Browser.add_menus(menus);
+
+        return this;
+      },
+
+      // Callback to draw Wizard Dialog
+      start_grant_wizard: function(action, item) {
+
+        // Declare Wizard dialog
+        if (!alertify.wizardDialog) {
+          alertify.dialog('wizardDialog', function factory() {
+
+            // Generate wizard main container
+            var $container = $("<div class='wizard_dlg'></div>");
+
+            return {
+              main: function(title) {
+                this.set('title', title);
+              },
+              setup:function() {
+                return {
+                  // Set options for dialog
+                  options: {
+                    frameless: true,
+                    resizable: true,
+                    autoReset: false,
+                    maximizable: false,
+                    closable: false,
+                    closableByDimmer: false,
+                    modal: false,
+                    pinnable: false
+                  }
+                };
+              },
+              hooks:{
+                onshow: function() {
+
+                  // Add pgadmin_grant_wizard_body class to dialog
+                  $(this.elements.body).addClass('pgadmin_grant_wizard_body');
+                }
+              },
+
+              /**
+                Returns a Paginator Class Object which is again to be rendered
+
+                @class {Backgrid.Extension.Paginator}
+                @param {Backbone.Collection} coll - from which data is fetched
+                @return {Object} paginator
+              */
+              DbPaginator: function(coll){
+                var paginator = this.paginator = new Backgrid.Extension.Paginator({
+                  collection: coll,
+                  windowSize: 8
+                });
+                return paginator;
+              },
+
+              /**
+                Create new Filter which will filter the
+                rendered grid for Select Type Tabular Data
+                @param {Backbone.PageableCollection} coll
+              */
+              DbObjectFilter: function(coll){
+                var clientSideFilter = this.clientSideFilter = new Backgrid.Extension.ClientSideFilter({
+                  collection: coll,
+                  placeholder: _('Search by object type or name'),
+
+                  // The model fields to search for matches
+                  fields: ['object_type', 'name'],
+
+                  // How long to wait after typing has stopped before searching can start
+                  wait: 150
+                });
+                return clientSideFilter;
+              },
+
+              //Enable Disable Next button of PrivilegePage
+              updateButtons: function(modified){
+                if(!modified)
+                  $('.wizard-next').prop('disabled', true);
+                else
+                  $('.wizard-next').prop('disabled', false);
+              },
+
+              /**
+                Callback called when an errorModel is set
+                with invalid value and errormsg is set into
+                status bar element and next button is disabled
+              */
+              onSessionInvalid: function(msg) {
+                $('.error_msg_div p').html(msg).removeClass("hide");
+
+                // Enable disable Next button
+                this.updateButtons(false);
+                return true;
+              },
+
+              /**
+                Callback called when anything is set into model
+                thus hide error msg element and enable next button
+                status bar element and next button is disabled
+              */
+              onSessionValidated: function(sessHasChanged) {
+                $('.error_msg_div p').empty().addClass("hide");
+
+                // Enable disable Next button
+                this.updateButtons(sessHasChanged);
+              },
+
+              /**
+                Remove/Delete objects, attributes
+                in wizard on wizard close or finish
+                to reclaim memory
+              */
+              releaseObjects: function(){
+                var self = this;
+
+                if(!_.isUndefined(self.dbObjectFilter)) {
+                  self.dbObjectFilter.remove();
+                  self.dbObjectFilter = undefined;
+                }
+
+                if(!_.isUndefined(self.clientSideFilter)) {
+                  self.clientSideFilter.remove();
+                  self.clientSideFilter = undefined;
+                }
+
+                // clear object priv array
+                if(!_.isNull(self.obj_priv) &&
+                  !_.isUndefined(self.obj_priv)) {
+                  self.obj_priv = [];
+                  delete self.obj_priv;
+                }
+
+                // Delete Wizard Pages, clear model and cleanup view
+                if(!_.isUndefined(self.dbObjectTypePage) &&
+                  !_.isNull(self.dbObjectTypePage)) {
+                  if(!_.isUndefined(self.dbObjectTypePage.get('model')) &&
+                    !_.isNull(self.dbObjectTypePage.get('model'))) {
+                    self.dbObjectTypePage.get('model').clear();
+                    self.dbObjectTypePage.get('view').cleanup();
+                    self.dbObjectTypePage = undefined;
+                  }
+                }
+
+                if(!_.isUndefined(self.privilegePage) &&
+                  !_.isNull(self.privilegePage)) {
+                  if(!_.isUndefined(self.privilegePage.get('model')) &&
+                    !_.isNull(self.privilegePage.get('model'))) {
+                    self.privilegePage.get('model').clear();
+                    self.privilegePage.get('view').cleanup();
+                    self.privilegePage = undefined;
+                  }
+                }
+
+                if(!_.isUndefined(self.reviewSQLPage) &&
+                  !_.isNull(self.reviewSQLPage)) {
+                  if(!_.isUndefined(self.reviewSQLPage.get('model')) &&
+                    !_.isNull(self.reviewSQLPage.get('model'))) {
+                    self.reviewSQLPage.get('model').clear();
+                    self.reviewSQLPage = undefined;
+                  }
+                }
+
+                // Remove Sql control
+                if (!_.isUndefined(self.sqlControl)) {
+                  self.sqlControl.remove();
+                }
+
+                // Clear privModel
+                if(!_.isNull(self.privModel) &&
+                  !_.isUndefined(self.privModel)) {
+                  self.privModel.clear();
+                }
+
+                // Remove collection containing db object data
+                if(!_.isNull(self.coll) &&
+                  !_.isUndefined(self.coll)) {
+                  self.coll.reset();
+                  self.coll = undefined;
+                }
+                // Delete Wizard
+                if(!_.isNull(self.wizard) &&
+                  !_.isUndefined(self.wizard)) {
+                  self.wizard.collection.reset();
+                  self.wizard.curr_page = undefined;
+                }
+
+              },
+
+              /**
+                Every time a wizard is opened, this function
+                is called everytime. It has Wizard Pages which
+                are rendered by the Wizard Class:
+
+                @class {pgBrowser.WizardPage} dbObjectType1 - This page
+                @extends {Backbone.Model}
+                renders a grid of Database Object Types such as
+                  Schemas, Views and Sequences etc.
+
+                @class {pgBrowser.WizardPage} WizardPage2 - This page
+                @extends {Backbone.Model}
+                adds Privilege Control which provides grant privileges
+                such as "Create, Insert, Delete, Update" so on the
+                database objects selected on Wizard Pages.
+
+                @class {pgBrowser.WizardPage} WizardPage3 - This page
+                displays the generated GRANT SQL query for the Db
+                objects selected with the specific privileges added to it.
+                @extends {Backbone.Model}
+
+                @class {Backbone.Collection} WizardCollection - It is the
+                collection of wizard pages
+
+                @class {pgBrowser.Wizard} wizard - Its task is:
+                - Create a Wizard
+                - Add Buttons, Callbacks to it.
+                - Render WizardPages
+                @extends {Backbone.View}
+
+              */
+              build: function() {
+                this.elements.content.appendChild($container.get(0));
+                alertify.pgDialogBuild.apply(this);
+              },
+
+              //Returns list of Acls defined for nodes
+              get_json_data: function(gid, sid, did) {
+                return $.ajax({
+                  async: false,
+                  url: url_for(
+                    'grant_wizard.acl',
+                    {'sid': encodeURI(sid), 'did': encodeURI(did)}
+                  ),
+                  dataType: 'jsonp'
+                });
+
+              },
+              prepare:function() {
+                var that = this;
+                $container.empty().append("<div class='grant_wizard_container'></div>");
+
+                // Define el for wizard view
+                var el = $('.grant_wizard_container');
+
+                // Extract the data from the selected tree node
+                var t = pgBrowser.tree,
+                    i = t.selected(),
+                    d = this.d = i && i.length == 1 ? t.itemData(i) : undefined,
+                    info = this.info = pgBrowser.Node.getTreeNodeHierarchy(i),
+                    icon = d.icon;
+
+                /**
+                  Generate a URL using:
+                  gid, did, sid(server id), node_id(node id),
+                  node_(node name), node_type(node type)
+                  and pass it to collection which will fetch Object Type properties.
+                */
+                var gid = info['server_group']._id,
+                    sid = info.server._id,
+                    did = info.database._id,
+                    node_id = d._id,
+
+                    /**
+                      get node name only. used in mapping with object types defined
+                      in allowed_acl.json
+                     */
+                    node_type = d._type.replace('coll-', '').replace('materialized_', ''),
+                    node_label = d.label;
+
+                // Fetch privileges specific to nodes
+                var json_data = this.get_json_data(gid, sid, did);
+                var privDict = JSON.parse(json_data.responseText);
+
+                // Collection url to fetch database object types for objects field
+                var baseUrl = url_for(
+                      'grant_wizard.objects', {
+                        'sid': encodeURI(sid), 'did': encodeURI(did),
+                        'node_id': encodeURI(node_id),
+                        'node_type': encodeURI(node_type)
+                      }),
+                    // Model's save url
+                    saveUrl = url_for(
+                      'grant_wizard.apply', {
+                        'sid': encodeURI(sid), 'did': encodeURI(did)
+                      }),
+                    // generate encoded url based on wizard type
+                    msql_url = this.msql_url = url_for(
+                      'grant_wizard.modified_sql', {
+                        'sid': encodeURI(sid), 'did': encodeURI(did)
+                      }),
+                    Coll = Backbone.Collection.extend({
+                      model: DatabaseObjectModel,
+                      url: baseUrl
+                    }),
+
+                    // Create instances of collection and filter
+                    coll = this.coll = new Coll(),
+                    self = this;
+
+                coll.comparator = function(model) {
+                  return model.get('object_type');
+                }
+
+                coll.sort();
+                var dbObjectFilter = this.dbObjectFilter = this.DbObjectFilter(coll);
+
+                /**
+                  privArray holds objects selected which further helps
+                  in creating privileges Model
+                */
+                self.privArray = [];
+
+                /**
+                  Override backgrid listener "backgrid:selected" to
+                  Add/Remove model to/from objects collection
+                */
+                coll.on('backgrid:selected', function(model, selected) {
+                  model.set('selected', selected);
+
+                  var object_type = model.get('object_type');
+                  switch (object_type)
+                  {
+                    case 'Function':
+                      object_type = 'function';
+                      break;
+                    case 'Trigger Function':
+                      object_type = 'function';
+                      break;
+                    case 'Procedure':
+                      object_type = 'procedure';
+                      break;
+                    case 'Table':
+                      object_type = 'table';
+                      break;
+                    case 'Sequence':
+                      object_type = 'sequence';
+                      break;
+                    case 'View':
+                      object_type = 'table';
+                      break;
+                    case 'Materialized View':
+                      object_type = 'table';
+                      break;
+                    default:
+                      break;
+                  }
+
+                  /**
+                    if a row (checkbox) is checked, add that model
+                    into collection, when unchecked remove it from
+                    model.
+
+                    Also push/pop object type in/from privArray
+                  */
+                  if(selected) {
+                    if(_.indexOf(self.privArray, object_type) == -1)
+                      self.privArray.push(object_type);
+                    newModel.get('objects').add(model, { silent: true });
+                  }
+                  else {
+                    var idx = self.privArray.indexOf(object_type);
+                    if(idx !=-1)
+                      self.privArray.splice(idx, 1);
+                    newModel.get('objects').remove(model);
+                  }
+
+                  // validate model on checkbox check/uncheck
+                  var msg =  model.validate.call(newModel);
+
+                  /**
+                    If no object type is selected, set error msg
+                    and disable next button, else enable next button
+                  */
+                  if(msg)
+                    self.onSessionInvalid.call(self, msg);
+                  else
+                    self.onSessionValidated.call(self, true);
+                });
+
+                /**
+                  It is the main model with schema defined
+                  Every time a new wizard is opened,
+                  a new model should create.
+                */
+                var GrantWizardModel = pgNode.Model.extend({
+                  defaults: {
+                    objects: undefined,
+                    acl: undefined
+                  },
+                  schema: [
+                    {
+                      id: 'objects', label: gettext('Objects'), model: DatabaseObjectModel,
+                      type: 'collection', group: gettext('Objects')
+                    },
+                    {
+                      id: 'acl', label: gettext('Privileges'),
+                      model: pgAdmin.Browser.Node.PrivilegeRoleModel,
+                      type: 'collection', canAdd: true,
+                      canDelete: true, control: 'unique-col-collection'
+                    }
+                  ],
+                  urlRoot: saveUrl
+                });
+
+                /**
+                  Create instance of GrantWizard Model, provide urlRoot
+                  node_info object, Generate fields objects
+                */
+                var newModel = new GrantWizardModel({}, { node_info: info });
+
+                /**
+                  Fetch data from server and set into grid
+                  and show/hide progress bar
+                */
+                $('.wizard-progress-bar p').show();
+
+                coll.fetch({
+                  success: function(collection, data) {
+                    $('.wizard-progress-bar p').html('');
+                    $('.wizard-progress-bar').hide();
+                  },
+                  reset: true
+                }, this);
+
+                //////////////////////////////////////////////////////////////////////
+                //                                                                  //
+                //            Wizard Page for Db Object Type                        //
+                //                                                                  //
+                //////////////////////////////////////////////////////////////////////
+
+                /**
+                  Create wizard page. It renders a grid of
+                  Database Object Types such as
+                  Schemas, Views and Sequences etc.
+                  Set default values
+                */
+                var dbObjectTypePage = self.dbObjectTypePage = new pgBrowser.WizardPage({
+                  id: 1,
+                  page_title: _('Object Selection (step 1 of 3)'),
+                  disable_prev: true,
+                  disable_next: true,
+                  show_description: _('Please select objects from the list below.'),
+                  show_progress_bar: _('Please wait while fetching records...'),
+                  model: newModel,
+                  view: new (function() {
+
+                    // Set page Instance
+                    var pageView = this;
+
+                    _.extend(pageView, {
+
+                      // Remove grid if it is before render
+                      cleanup: function() {
+                        if (this.grid) {
+                          this.grid.remove();
+                          delete this.grid;
+                          this.grid = null;
+                        }
+
+                        // Remove grid element if exists
+                        if (this.el) {
+                          $(this.el).remove();
+                          delete this.el;
+                        }
+                      },
+
+                      // Delete grid before render
+                      grid: null,
+
+                      render: function() {
+
+                        // Create a grid container
+                        var gridBody =
+                              $('<div class="db_objects_container pg-el-xs-12"></div>');
+
+                        // Remove grid if exits before render
+                        if (this.grid) {
+                          this.cleanup();
+                        }
+
+                        // Initialize a new Grid instance
+                        this.grid = new Backgrid.Grid({
+                          columns: _.clone(columns),
+                          collection: coll,
+                          className: "backgrid table-bordered object_type_table pg-el-xs-12"
+                          });
+
+                        // Render selection Type grid and paginator
+                        gridBody.append( this.grid.render().$el);
+
+                        // Render Search Filter
+                        gridBody.prepend(
+                          self.clientSideFilter.render().el);
+
+                        // Assign gridBody content to page element
+                        this.el = gridBody;
+
+                        /**
+                          Fetch selected models from collection and
+                          make rows checked in grid
+                        */
+                        newModel.get('objects').each(function(m) {
+                          var model = coll.get(m.get('object_id'));
+                          if (model) {
+                            coll.trigger('backgrid:selected', model, true);
+                          }
+                        });
+
+                        // Refresh grid to re render rows.
+                        coll.trigger('backgrid:refresh');
+
+                        return this;
+                      }
+                    });
+                  }),
+
+                  beforeNext: function(obj){
+                    var self = this;
+                    obj.options.disable_next = true;
+
+                    /**
+                      Enable/Disable next button of privilegePage if objects
+                      are present in model
+                    */
+                    if(!_.isNull(newModel.get('acl')) &&
+                      !_.isUndefined(newModel.get('acl'))) {
+                      if(newModel.get('acl').length > 0)
+                        obj.collection.at(1).set('disable_next', false);
+                      }
+
+                    // Clean the view
+                    if (self.view) {
+                      self.view.cleanup();
+                      delete self.view;
+                      self.view = null;
+                    }
+                    return true;
+                  },
+
+                });
+
+                //////////////////////////////////////////////////////////////////////
+                //                                                                  //
+                //            Wizard Page for Privilege Control                     //
+                //                                                                  //
+                //////////////////////////////////////////////////////////////////////
+
+                // Wizard for Privelege control
+                var privilegePage = self.privilegePage = new pgBrowser.WizardPage({
+                  id: 2,
+                  page_title: _('Privileges Selection (step 2 of 3)'),
+                  show_description: _('Please select privileges for the selected objects.'),
+                  disable_next: true,
+                  model: newModel,
+
+                  // Create a view function object
+                  view: new (function() {
+                    var pageView = this;
+                      _.extend(pageView, {
+
+                        // Render Privelege control to generate its html markup
+                        render: function() {
+
+                          var obj_priv = [];
+                          self.privArray = _.uniq(self.privArray);
+                          _.each(self.privArray, function(priv){
+                            self.obj_priv = obj_priv = _.union(obj_priv , privDict[priv].acl);
+                          });
+
+                          /**
+                            Define PrivModel and its instance.
+                            Privileges array is generated based on
+                            the type of nodes selected.
+                           */
+                          var privModel = self.privModel;
+                          var PrivModel = pgNode.Model.extend({
+                            defaults: {
+                              acl: undefined
+                            },
+                            schema: [
+                              {
+                                id: 'acl', label: gettext('Privileges'),
+                                model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({
+
+                                  // privileges are selected based on node clicked
+                                  privileges: obj_priv
+                                }), uniqueCol : ['grantee', 'grantor'], editable: true,
+                                type: 'collection', canAdd: true,
+                                canDelete: true, control: 'unique-col-collection'
+                              }
+                            ]
+                          });
+
+                          /**
+                            When privelege control is re-rendered, in order to
+                            render privileges based on object type selected,
+                            delete privileges from privModel which are now not
+                            present in object privileges array(object_priv)
+                           */
+                          var data = {};
+                          if (privModel) {
+                            data = privModel.toJSON();
+                            var rolePrivs = data['acl'];
+                            if (!_.isUndefined(rolePrivs) && rolePrivs.length > 0) {
+                              for (var idx in rolePrivs) {
+                                var rolePriv = (rolePrivs[idx])['privileges'],
+                                    removeIdx = [], j;
+
+                                for (j in rolePriv) {
+                                  var p = rolePriv[j];
+                                  if (_.indexOf(obj_priv, p['privilege_type']) == -1) {
+                                    removeIdx.push(j);
+                                  }
+                                }
+
+                                for (j in removeIdx) {
+                                  rolePriv.splice(j, 1);
+                                }
+                              }
+                            } else {
+                              console.log('Acls are not defined');
+                            }
+                          }
+
+                          // Instantiate privModel
+                          privModel = self.privModel = new PrivModel(data, { node_info: self.info });
+
+                          /*
+                          To track changes into model, start new session
+                          and Add event listener for privileges control
+                          */
+                          self.privModel.startNewSession();
+                          self.privModel.on('pgadmin-session:valid', self.onSessionValidated.bind(self));
+                          self.privModel.on('pgadmin-session:invalid', self.onSessionInvalid.bind(self));
+
+                        /**
+                          Create Field Object which has properties like
+                          node_data, node_info which is required for rendering
+                          Privilege control
+                          */
+                          var fields = Backform.generateViewSchema(
+                              self.info, self.privModel, 'create', self.d._type, self.d
+                              );
+                          var privilegesField = new Backform.Field(fields[0].fields[0]);
+
+                          this.privControl = new (privilegesField.get('control')) ({
+                            field: privilegesField,
+                            model: self.privModel
+                          });
+
+                          return {el: this.privControl.render().$el};
+                        },
+
+                        // Remove the privilege control
+                        cleanup: function() {
+                          if (this.privControl) {
+                            this.privControl.remove();
+                            delete this.privControl;
+                            this.privControl = null;
+                          }
+                        }
+                      });
+                  }),
+
+                  beforePrev: function(wizardObj) {
+
+                    // Remove the privilege control
+                    if (this.view) {
+                      this.view.cleanup();
+                      delete this.view;
+                      this.view = null;
+                    }
+
+                    /**
+                      Enable/Disable next button of DbObjectType page if objects
+                      are present in model
+                     */
+                    var objectsModel = newModel.get('objects');
+
+                    if(!_.isUndefined(objectsModel) && !_.isEmpty(objectsModel) &&
+                        objectsModel.length > 0) {
+                      wizardObj.collection.at(0).set('disable_next', false);
+
+                      // Don't show progress bar
+                      wizardObj.collection.at(0).set('show_progress_bar', '');
+                    }
+
+                    /**
+                      We're re-rendering the controls as they are deleted
+                      before heading to next page
+                      Refresh Backgrid to re-render the elements selected
+                      re-render Filter
+                    */
+                    newModel.trigger("backgrid:refresh", newModel, false);
+                    self.clientSideFilter.render();
+                    return true;
+                  },
+
+                  beforeNext: function() { return true; },
+
+                  onNext: function(obj){
+
+                    // Assign acls of privModel to main model newModel
+                    if (!_.isUndefined(self.privModel)) {
+                      newModel.set({'acl': self.privModel.get('acl')});
+                    }
+
+                    // Remove the privilege control
+                    if (this.view) {
+                      this.view.cleanup();
+                      delete this.view;
+                      this.view = null;
+                    }
+
+                    // Enable finish button
+                    self.wizard.options.disable_finish = false;
+
+                    /**
+                      triggers to get SQL queries data to render
+                      into the reviewSQLPage
+                    */
+                    newModel.trigger('pgadmin-wizard:nextpage:sql', {'node_type': node_type });
+                  }
+                });
+
+
+                //////////////////////////////////////////////////////////////////////
+                //                                                                  //
+                //            Review SQL Query Page                                 //
+                //                                                                  //
+                //////////////////////////////////////////////////////////////////////
+
+                //Create SqlField Object
+                var sqlField = new Backform.Field(
+                    {
+                      id: 'sqltab',
+                      label: _('Sql Tab'),
+
+                      /**
+                        Extend 'SqlTabControl' to define new
+                        function 'onWizardNextPageChange'
+                        which gets triggered on next button
+                        click to fetch generated SQL query
+                        for the selected db objects.
+                      */
+                      control: Backform.SqlTabControl.extend({
+                        initialize: function() {
+
+                          // Initialize parent class
+                          Backform.SqlTabControl.prototype.initialize.apply(this, arguments);
+
+                          this.msql_url = self.msql_url;
+
+                          // define trigger events for prev and next page
+                          this.model.on('pgadmin-wizard:nextpage:sql', this.onWizardNextPageChange, this);
+                          this.model.on('pgadmin-wizard:prevpage:sql', this.onWizardPrevPageChange, this);
+                        },
+
+                        // This method fetches the modified SQL for the wizard
+                        onWizardNextPageChange: function(){
+
+                          var self = this;
+
+                          // Fetches modified SQL
+                          $.ajax({
+                            url: this.msql_url,
+                            type: 'GET',
+                            cache: false,
+                            data: self.model.toJSON(true, 'GET'),
+                            dataType: "json",
+                            contentType: "application/json"
+                          }).done(function(res) {
+                            self.sqlCtrl.clearHistory();
+                            self.sqlCtrl.setValue(res.data);
+                            self.sqlCtrl.refresh();
+                          }).fail(function() {
+                            self.model.trigger('pgadmin-view:msql:error');
+                          }).always(function() {
+                            self.model.trigger('pgadmin-view:msql:fetched');
+                          });
+                        },
+
+                        remove: function() {
+
+                          // Clear html dom elements of CodeMirror sql tab
+                          self.sqlControl.unbind(); // Unbind all local event bindings
+                          var cmElem = self.sqlControl.sqlCtrl.getWrapperElement();
+                          $(cmElem).remove();
+                          self.sqlControl.sqlCtrl = undefined;
+                        }
+
+                      })
+                    }),
+
+                  /**
+                    Create sqlField view instance
+                    to render it into wizard page
+                  */
+                  sqlControl = self.sqlControl = new (sqlField.get('control'))({
+                    field: sqlField,
+                    model: newModel
+                  });
+
+                // Wizard for SQL tab control
+                var reviewSQLPage = self.reviewSQLPage = new pgBrowser.WizardPage({
+                  id: 3,
+                  page_title: _('Final (Review Selection) (step 3 of 3)'),
+                  show_description: _('The SQL below will be executed on the ' +
+                                'database server to grant the selected privileges. ' +
+                                'Please click on <b>Finish</b> to complete the process. '),
+                  model: newModel,
+                  view: new(function() {
+
+                    // Render SqlTab control to generate its html markup
+                    var sqlCtrlHtml = sqlControl.render().$el;
+                    this.render = function() {
+                        return { el: sqlCtrlHtml };
+                    };
+                  }),
+
+                  beforePrev: function(wizardObj) {
+
+                    /**
+                      Enable next button if privilege
+                      model is not empty else disable
+                      next button
+                     */
+                    var aclModel = newModel.get('acl');
+
+                    if(!_.isUndefined(wizardObj.collection) &&
+                      wizardObj.collection.models.length > 0) {
+                      if(!_.isUndefined(aclModel) && !_.isEmpty(aclModel) &&
+                          aclModel.length > 0) {
+                        wizardObj.collection.at(1).set('disable_next', false);
+                      }
+                      else {
+                        wizardObj.collection.at(1).set('disable_next', true);
+                      }
+
+                      return true;
+                    }
+                  },
+                });
+
+
+                // Create Wizard Collection of Wizard Pages
+                var WizardCollection = Backbone.Collection.extend({
+                  model: pgBrowser.WizardPage
+                });
+
+                // It holds all the wizard pages to be rendered
+                this.wizardCollection = new WizardCollection(
+                  [dbObjectTypePage, privilegePage, reviewSQLPage]
+                );
+
+                /**
+                  Create wizard which has following operations:
+                  - renders wizard pages
+                  - defines the first page to render in wizard
+                  - Save the model on finishbutton
+                  - Remove wizard on cancel button
+                  */
+                self.wizard = new (pgBrowser.Wizard.extend({
+                  options: {
+                    title: _('Grant Wizard'), /* Main Wizard Title */
+                    width: '',
+                    height: '',
+                    curr_page: 0,
+                    show_left_panel: false,
+                    show_header_cancel_btn: true,
+                    show_header_maximize_btn: true,
+                    disable_finish: true,
+                    dialog_api: that,
+                    wizard_help: url_for(
+                      'help.static', {'filename': 'grant_wizard.html'}
+                    )
+                  },
+
+                  // Callback for finish button
+                  onFinish: function() {
+                    var m = newModel,
+                        d = m.toJSON('GET');
+
+                    // Save model
+                    if (d && !_.isEmpty(d) && !_.isUndefined(d.objects)) {
+                      m.save({}, {
+                        attrs: d,
+                        validate: false,
+                        cache: false,
+                        success: function(res) {
+
+                          // Release wizard objects
+                          self.releaseObjects();
+                          self.close();
+                        },
+                        error: function(m, jqxhr) {
+                          alertify.pgNotifier(
+                            "error", jqxhr,
+                            S(
+                              gettext("Error saving properties: %s")
+                              ).sprintf(jqxhr.statusText).value()
+                            );
+
+                            // Release wizard objects
+                            self.releaseObjects();
+                            self.close();
+                        }
+                      });
+                    }
+                  },
+
+                  // Callback for cancel button
+                  onCancel: function() {
+
+                    // Release wizard objects
+                    self.releaseObjects();
+                    self.close();
+                  }
+                })) ({
+                  collection: this.wizardCollection,
+                  el: el,
+                  model: newModel
+                });
+
+                // Render wizard
+                self.wizard.render();
+              }
+            };
+          });
+        }
+
+        // Call Grant Wizard Dialog and set dimensions for wizard
+        alertify.wizardDialog(true).resizeTo('55%', '75%');
+      }
+    };
+
+    return pgBrowser.GrantWizard;
+  });
diff --git a/web/pgadmin/tools/import_export/__init__.py b/web/pgadmin/tools/import_export/__init__.py
index 931e86fb..b7eb60d9 100644
--- a/web/pgadmin/tools/import_export/__init__.py
+++ b/web/pgadmin/tools/import_export/__init__.py
@@ -47,8 +47,9 @@ class ImportExportModule(PgAdminModule):
         ]:
             scripts.append({
                 'name': name,
-                'path': url_for('import_export.index') + script,
-                'when': None
+                'path': os.path.join(self.root_path, 'static/%s' % script),
+                'when': None,
+                'is_template': False
             })
 
         return scripts
diff --git a/web/pgadmin/tools/import_export/static/js/import_export.js b/web/pgadmin/tools/import_export/static/js/import_export.js
new file mode 100644
index 00000000..429b5ed7
--- /dev/null
+++ b/web/pgadmin/tools/import_export/static/js/import_export.js
@@ -0,0 +1,542 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'backform',
+  'sources/alerts/alertify_wrapper',
+
+  'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui'
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, Backform, AlertifyWrapper
+) {
+
+  pgAdmin = pgAdmin || window.pgAdmin || {};
+
+  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+  // Return back, this has been called more than once
+  if (pgAdmin.Tools.import_utility)
+    return pgAdmin.Tools.import_utility;
+
+  // Main model for Import/Export functionality
+  var ImportExportModel = Backbone.Model.extend({
+    defaults: {
+      is_import: false, /* false for Export */
+      filename: undefined,
+      format: 'csv',
+      encoding: undefined,
+      oid: undefined,
+      header: undefined,
+      delimiter: '',
+      quote: '\"',
+      escape: '\'',
+      null_string: undefined,
+      columns: null,
+      icolumns: [],
+      database: undefined,
+      schema: undefined,
+      table: undefined
+    },
+    schema: [{
+      id: 'is_import', label: gettext('Import/Export'), cell: 'switch',
+      type: 'switch', group: gettext('Options'),
+      options: {
+         'onText': gettext('Import'), 'offText': gettext('Export'),
+         'onColor': 'success', 'offColor': 'primary'
+      }
+    }, {
+      type: 'nested', control: 'fieldset', label: gettext('File Info'),
+      group: gettext('Options'),
+      schema:[{ /* select file control for import */
+      id: 'filename', label: gettext('Filename'), deps: ['is_import'],
+      type: 'text', control: Backform.FileControl, group: gettext('File Info'),
+      dialog_type: 'select_file', supp_types: ['csv', 'txt', '*'],
+      visible: 'importing'
+    }, { /* create file control for export */
+        id: 'filename', label: gettext('Filename'), deps: ['is_import'],
+        type: 'text', control: Backform.FileControl, group: gettext('File Info'),
+        dialog_type: 'create_file', supp_types: ['csv', 'txt', '*'],
+        visible: 'exporting'
+      }, {
+        id: 'format', label: gettext('Format'), cell: 'string',
+        control: 'select2', group: gettext('File Info'),
+        options:[
+            {'label': 'binary', 'value': 'binary'}, {'label': 'csv', 'value': 'csv'}, {'label': 'text', 'value': 'text'},
+          ],
+        disabled: 'isDisabled', select2: {allowClear: false, width: "100%" },
+      }, {
+        id: 'encoding', label: gettext('Encoding'), cell: 'string',
+        control: 'node-ajax-options', node: 'database', url: 'get_encodings', first_empty: true,
+        group: gettext('File Info')
+      }]
+    },{
+        id: 'columns', label: gettext('Columns to import'), cell: 'string',
+        deps: ['is_import'], type: 'array', first_empty: false,
+        control: Backform.NodeListByNameControl.extend({
+          // By default, all the import columns should be selected
+          initialize: function() {
+            Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments);
+            var self = this,
+                options = self.field.get('options'),
+                op_vals = [];
+
+            if (_.isFunction(options)) {
+              try {
+                var all_cols = options.apply(self);
+                for(var idx in all_cols) {
+                  op_vals.push((all_cols[idx])['value']);
+                }
+              } catch(e) {
+                // Do nothing
+                options = [];
+              }
+            } else {
+              for (idx in options) {
+                op_vals.push((options[idx])['value']);
+              }
+            }
+
+            self.model.set('columns',op_vals);
+          }
+        }),
+        transform: function(rows) {
+          var self = this,
+              node = self.field.get('schema_node'),
+              res = [];
+
+          _.each(rows, function(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;
+        },
+        node: 'column', url: 'nodes', group: gettext('Columns'),
+        select2: {
+          multiple: true, allowClear: false,
+          placeholder: gettext('Columns for importing...'),
+          first_empty: false
+        }, visible: 'importing',
+        helpMessage:
+        gettext('An optional list of columns to be copied. If no column list is specified, all columns of the table will be copied.')
+      }, {
+        id: 'columns', label: gettext('Columns to export'), cell: 'string',
+        deps: ['is_import'], type: 'array',
+        control: 'node-list-by-name', first_empty: false,
+        node: 'column', url: 'nodes', group: gettext('Columns'),
+        select2: {
+          multiple: true, allowClear: true,
+          placeholder: gettext('Colums for exporting...')
+        }, visible: 'exporting',
+        transform: function(rows) {
+          var self = this,
+              node = self.field.get('schema_node'),
+              res = [];
+
+          _.each(rows, function(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;
+        },
+        helpMessage:
+          gettext('An optional list of columns to be copied. If no column list is specified, all columns of the table will be copied.')
+      }, {
+        id: 'null_string', label: gettext('NULL Strings'), cell: 'string',
+        type: 'text', group: gettext('Columns'), disabled: 'isDisabled',
+        deps: ['format'],
+        helpMessage:
+          gettext("Specifies the string that represents a null value. The default is \\N (backslash-N) in text format, and an unquoted empty string in CSV format. You might prefer an empty string even in text format for cases where you don't want to distinguish nulls from empty strings. This option is not allowed when using binary format.")
+      }, {
+        id: 'icolumns', label: gettext('Not null columns'), cell: 'string',
+        control: 'node-list-by-name', node: 'column',
+        group: gettext('Columns'), deps: ['format', 'is_import'], disabled: 'isDisabled',
+        type: 'array', first_empty: false,
+        select2: {
+          multiple: true, allowClear: true, first_empty: true,
+          placeholder: gettext('Not null columns...')
+        },
+        helpMessage:
+          gettext('Do not match the specified column values against the null string. In the default case where the null string is empty, this means that empty values will be read as zero-length strings rather than nulls, even when they are not quoted. This option is allowed only in import, and only when using CSV format.')
+      }, {
+        type: 'nested', control: 'fieldset', label: gettext('Miscellaneous'),
+        group: gettext('Options'),
+        schema:[{
+          id: 'oid', label: gettext('OID'), cell: 'string',
+          type: 'switch', group: gettext('Miscellaneous')
+        },{
+          id: 'header', label: gettext('Header'), cell: 'string',
+          type: 'switch', group: gettext('Miscellaneous'), deps: ['format'], disabled: 'isDisabled'
+        },{
+          id: 'delimiter', label: gettext('Delimiter'), cell: 'string', first_empty: true,
+          type: 'text', control: 'node-ajax-options', group: gettext('Miscellaneous'), disabled: 'isDisabled',
+          deps: ['format'],
+          options:[
+            {'label': ';', 'value': ';'},
+            {'label': ',', 'value': ','},
+            {'label': '|', 'value': '|'},
+            {'label': '[tab]', 'value': '[tab]'}
+          ],
+          select2: {
+            tags: true,
+            allowClear: false,
+            width: "100%",
+            placeholder: gettext('Select from list...')
+          }, helpMessage:
+            gettext('Specifies the character that separates columns within each row (line) of the file. The default is a tab character in text format, a comma in CSV format. This must be a single one-byte character. This option is not allowed when using binary format.')
+        },
+        {
+          id: 'quote', label: gettext('Quote'), cell: 'string', first_empty: true, deps: ['format'],
+          type: 'text', control: 'node-ajax-options', group: gettext('Miscellaneous'), disabled: 'isDisabled',
+          options:[
+            {'label': '\"', 'value': '\"'},
+            {'label': '\'', 'value': '\''},
+          ],
+          select2: {
+            tags: true,
+            allowClear: false,
+            width: "100%",
+            placeholder: gettext('Select from list...')
+          }, helpMessage:
+            gettext('Specifies the quoting character to be used when a data value is quoted. The default is double-quote. This must be a single one-byte character. This option is allowed only when using CSV format.')
+        },
+        {
+          id: 'escape', label: gettext('Escape'), cell: 'string', first_empty: true, deps: ['format'],
+          type: 'text', control: 'node-ajax-options', group: gettext('Miscellaneous'), disabled: 'isDisabled',
+          options:[
+            {'label': '\"', 'value': '\"'},
+            {'label': '\'', 'value': '\''},
+          ],
+          select2: {
+            tags: true,
+            allowClear: false,
+            width: "100%",
+            placeholder: gettext('Select from list...')
+          }, helpMessage:
+            gettext('Specifies the character that should appear before a data character that matches the QUOTE value. The default is the same as the QUOTE value (so that the quoting character is doubled if it appears in the data). This must be a single one-byte character. This option is allowed only when using CSV format.')
+        }]
+      }
+    ],
+
+    // Enable/Disable the items based on the user file format selection
+    isDisabled: function(m) {
+      name = this.name;
+      switch(name) {
+        case 'quote':
+        case 'escape':
+        case 'header':
+          return (m.get('format') != 'csv')
+        case 'icolumns':
+          return (m.get('format') != 'csv' || !m.get('is_import'));
+        case 'null_string':
+        case 'delimiter':
+          return (m.get('format') == 'binary');
+        default:
+          return false;
+      }
+    },
+    importing: function(m) {
+      return m.get('is_import');
+    },
+    exporting: function(m) {
+      return !(m.importing.apply(this, arguments));
+    }
+  });
+
+  pgTools.import_utility = {
+      init: function() {
+        // We do not want to initialize the module multiple times.
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        /**
+         Enable/disable import menu in tools based on node selected
+         Import menu will be enabled only when user select table node.
+        */
+        var menu_enabled = function(itemData, item, data) {
+         var t = pgBrowser.tree, i = item, d = itemData;
+         var parent_item = t.hasParent(i) ? t.parent(i): null,
+             parent_data = parent_item ? t.itemData(parent_item) : null;
+           if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data))
+             return (
+               (_.indexOf(['table'], d._type) !== -1 &&
+               parent_data._type != 'catalog') ? true: false
+             );
+           else
+             return false;
+        };
+
+        // Initialize the context menu to display the import options when user open the context menu for table
+        pgBrowser.add_menus([{
+          name: 'import', node: 'table', module: this,
+          applies: ['tools', 'context'], callback: 'callback_import_export',
+          category: 'import', priority: 10, label: gettext('Import/Export...'),
+          icon: 'fa fa-shopping-cart', enable: menu_enabled
+        }]);
+      },
+
+      /*
+        Open the dialog for the import functionality
+      */
+      callback_import_export: function(args, item) {
+        var i = item || pgBrowser.tree.selected(),
+          server_data = null;
+
+        while (i) {
+          var node_data = pgBrowser.tree.itemData(i);
+          if (node_data._type == 'server') {
+            server_data = node_data;
+            break;
+          }
+
+          if (pgBrowser.tree.hasParent(i)) {
+            i = $(pgBrowser.tree.parent(i));
+          } else {
+            Alertify.alert(gettext("Please select server or child node from tree."));
+            break;
+          }
+        }
+
+        if (!server_data) {
+          return;
+        }
+
+        var module = 'paths',
+          preference_name = 'pg_bin_dir',
+          msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+        if ((server_data.type && server_data.type == 'ppas') ||
+            server_data.server_type == 'ppas') {
+          preference_name = 'ppas_bin_dir';
+          msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+        }
+
+        var preference = pgBrowser.get_preference(module, preference_name);
+
+        if(preference) {
+          if (!preference.value) {
+            Alertify.alert(gettext('Configuration required'), msg);
+            return;
+          }
+        } else {
+          Alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
+          return;
+        }
+
+        var self = this;
+        var input = args || {},
+            t = pgBrowser.tree,
+            i = item || t.selected(),
+            d = i && i.length == 1 ? t.itemData(i) : undefined,
+            node = d && pgBrowser.Nodes[d._type];
+
+        if (!d)
+          return;
+
+        var objName = d.label;
+        var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+        if (!Alertify.ImportDialog) {
+          Alertify.dialog('ImportDialog', function factory() {
+
+            return {
+              main: function(title, node, item, data) {
+                this.set('title', title);
+                this.setting('pg_node', node);
+                this.setting('pg_item', item);
+                this.setting('pg_item_data', data);
+              },
+
+              build: function() {
+                Alertify.pgDialogBuild.apply(this)
+              },
+
+              setup: function() {
+                return {
+                  buttons:[{
+                    text: gettext("OK"), key: 13, disable: true,
+                    'data-btn-name': 'ok',
+                    className:
+                      "btn btn-primary fa fa-lg fa-save pg-alertify-button"
+                  }, {
+                    text: gettext("Cancel"), key: 27,
+                    'data-btn-name': 'cancel',
+                    className:
+                      "btn btn-danger fa fa-lg fa-times pg-alertify-button"
+                  }],
+                  options: {modal: true}
+                };
+              },
+
+              settings: {
+                pg_node: null,
+                pg_item: null,
+                pg_item_data: null
+              },
+
+              // Callback functions when click on the buttons of the Alertify dialogs
+              callback: function(e) {
+                if (e.button['data-btn-name'] === "ok") {
+
+                  var n = this.settings['pg_node'],
+                    i = this.settings['pg_item'],
+                    treeInfo = n.getTreeNodeHierarchy.apply(n, [i])
+
+                  this.view.model.set({
+                    'database': treeInfo.database._label,
+                    'schema': treeInfo.schema._label,
+                    'table': treeInfo.table._label
+                  });
+                  var self = this;
+
+                  $.ajax({
+                    url: url_for(
+                      'import_export.create_job', {'sid': treeInfo.server._id}
+                    ),
+                    method: 'POST',
+                    data:{ 'data': JSON.stringify(this.view.model.toJSON()) },
+                    success: function(res) {
+                      if (res.success) {
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.success(gettext('Import/export job created.'), 5);
+                        pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+                      }
+                    },
+                    error: function(xhr, status, error) {
+                      try {
+                        var err = $.parseJSON(xhr.responseText);
+                        Alertify.alert(
+ gettext('Import/export job failed.'),
+                          err.errormsg
+                        );
+                      } catch (e) {}
+                    }
+                  });
+                }
+              },
+
+              hooks: {
+                onclose: function() {
+                  if (this.view) {
+                    this.view.remove({data: true, internal: true, silent: true});
+                  }
+                },
+
+                // triggered when a dialog option gets update.
+                onupdate: function(option,oldValue, newValue) {
+
+                  switch(option){
+                    case 'resizable':
+                      if(newValue){
+                        this.elements.content.removeAttribute('style');
+                      } else {
+                        this.elements.content.style.minHeight = 'inherit';
+                      }
+                      break;
+                  }
+                }
+              },
+
+              prepare: function() {
+                // Main import module container
+                var self = this;
+
+                // Disable OK button until user provides valid Filename
+                this.__internal.buttons[0].element.disabled = true;
+
+                var $container = $("<div class='import_dlg'></div>"),
+                    n = this.settings.pg_node,
+                    i = this.settings.pg_item,
+                    treeInfo = n.getTreeNodeHierarchy.apply(n, [i]),
+                    newModel = new ImportExportModel ({}, {
+                      node_info: treeInfo
+                    }),
+                    fields = Backform.generateViewSchema(
+                        treeInfo, newModel, 'create', node, treeInfo.server, true
+                    ),
+                    view = this.view = new Backform.Dialog({
+                      el: $container, model: newModel, schema: fields
+                    });
+
+                $(this.elements.body.childNodes[0]).addClass(
+                  'alertify_tools_dialog_properties obj_properties'
+                );
+                view.render();
+
+                this.elements.content.appendChild($container.get(0));
+
+                // Listen to model & if filename is provided then enable OK button
+                // For the 'Quote', 'escape' and 'delimiter' only one character is allowed to enter
+                this.view.model.on('change', function() {
+                    if (!_.isUndefined(this.get('filename')) && this.get('filename') !== '') {
+                      this.errorModel.clear();
+                      if (!_.isUndefined(this.get('delimiter')) && !_.isNull(this.get('delimiter'))
+                      ) {
+                        this.errorModel.clear();
+                        if (!_.isUndefined(this.get('quote')) && this.get('quote') !== '' &&
+                            this.get('quote').length == 1) {
+                          this.errorModel.clear();
+                          if (!_.isUndefined(this.get('escape')) && this.get('escape') !== '' &&
+                              this.get('escape').length == 1) {
+                            this.errorModel.clear();
+                            self.__internal.buttons[0].element.disabled = false;
+                          } else {
+                            self.__internal.buttons[0].element.disabled = true;
+                            this.errorModel.set('escape', gettext('Escape should contain only one character'))
+                          }
+                        } else {
+                          self.__internal.buttons[0].element.disabled = true;
+                          this.errorModel.set('quote', gettext('Quote should contain only one character'))
+                        }
+                      } else {
+                        self.__internal.buttons[0].element.disabled = true;
+                        this.errorModel.set('delimiter', gettext('Delimiter should contain only one character'))
+                      }
+                    } else {
+                      self.__internal.buttons[0].element.disabled = true;
+                      this.errorModel.set('filename', gettext('Please provide filename'))
+                    }
+                });
+
+                // Give the dialog initial height & width
+                this.elements.dialog.style.minHeight = '80%';
+                this.elements.dialog.style.minWidth = '70%';
+              }
+            };
+          });
+        }
+
+        // Open the Alertify dialog for the import/export module
+        Alertify.ImportDialog(
+            S(
+ gettext("Import/Export data - table '%s'")
+            ).sprintf(treeInfo.table.label).value(), node, i, d
+         ).set('resizable',true).resizeTo('70%','80%');
+      }
+    };
+
+    return pgAdmin.Tools.import_utility;
+  });
diff --git a/web/pgadmin/tools/maintenance/__init__.py b/web/pgadmin/tools/maintenance/__init__.py
index 6c62b830..fee28adb 100644
--- a/web/pgadmin/tools/maintenance/__init__.py
+++ b/web/pgadmin/tools/maintenance/__init__.py
@@ -9,6 +9,7 @@
 
 """A blueprint module implementing the maintenance tool for vacuum"""
 
+import os
 import simplejson as json
 
 from flask import url_for, Response, render_template, request, current_app
@@ -49,8 +50,9 @@ class MaintenanceModule(PgAdminModule):
         ]:
             scripts.append({
                 'name': name,
-                'path': url_for('maintenance.index') + script,
-                'when': None
+                'path': os.path.join(self.root_path, 'static/%s' % script),
+                'when': None,
+                'is_template': False
             })
 
         return scripts
diff --git a/web/pgadmin/tools/maintenance/static/js/maintenance.js b/web/pgadmin/tools/maintenance/static/js/maintenance.js
new file mode 100644
index 00000000..9b5dc29a
--- /dev/null
+++ b/web/pgadmin/tools/maintenance/static/js/maintenance.js
@@ -0,0 +1,434 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'alertify', 'pgadmin', 'pgadmin.browser', 'backbone',
+  'backgrid', 'backform',
+  'sources/alerts/alertify_wrapper',
+
+  'pgadmin.backform', 'pgadmin.backgrid',
+  'pgadmin.browser.node.ui'
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
+  Backform, AlertifyWrapper
+) {
+
+  pgAdmin = pgAdmin || window.pgAdmin || {};
+
+  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+  // Return back, this has been called more than once
+  if (pgAdmin.Tools.maintenance)
+    return pgAdmin.Tools.maintenance;
+
+  var CustomSwitchControl = Backform.CustomSwitchControl = Backform.SwitchControl.extend({
+      template: _.template([
+        '<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
+        '<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
+        '  <div class="checkbox">',
+        '    <label>',
+        '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+        '    </label>',
+        '  </div>',
+        '</div>',
+        '<% if (helpMessage && helpMessage.length) { %>',
+        '  <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+        '<% } %>'
+      ].join("\n")),
+      className: 'pgadmin-control-group form-group col-xs-6'
+    });
+
+  // Main model for Maintenance functionality
+  var MaintenanceModel = Backbone.Model.extend({
+    defaults: {
+      op: 'VACUUM',
+      vacuum_full: false,
+      vacuum_freeze: false,
+      vacuum_analyze: false,
+      verbose: true
+    },
+    initialize: function() {
+        var node_info = arguments[1]['node_info'];
+        // If node is Unique or Primary key then set op to reindex
+        if ('primary_key' in node_info || 'unique_constraint' in node_info
+                                       || 'index' in node_info) {
+          this.set('op', 'REINDEX');
+          this.set('verbose', false);
+        }
+    },
+    schema: [
+      {
+        id: 'op', label: gettext('Maintenance operation'), cell: 'string',
+        type: 'text', group: gettext('Options'),
+        options:[
+          {'label': "VACUUM", 'value': "VACUUM"},
+          {'label': "ANALYZE", 'value': "ANALYZE"},
+          {'label': "REINDEX", 'value': "REINDEX"},
+          {'label': "CLUSTER", 'value': "CLUSTER"},
+        ],
+        control: Backform.RadioControl.extend({
+          template: _.template([
+            '<label class="control-label col-sm-4 col-xs-12"><%=label%></label>',
+            '<div class="pgadmin-controls col-xs-12 col-sm-8 btn-group pg-maintenance-op" data-toggle="buttons">',
+            ' <% for (var i=0; i < options.length; i++) { %>',
+            ' <% var option = options[i]; %>',
+            ' <label class="btn btn-primary<% if (i == 0) { %> active<%}%>">',
+            '  <input type="radio" name="op" id="op" autocomplete="off" value=<%-formatter.fromRaw(option.value)%><% if (i == 0) { %> selected<%}%> > <%-option.label%>',
+            ' </label>',
+            ' <% } %>',
+            '</div>'
+          ].join("\n"))
+        }),
+        select2: {
+          allowClear: false,
+          width: "100%",
+          placeholder: gettext('Select from list...')
+        },
+      },
+      {
+        type: 'nested', control: 'fieldset', label: gettext('Vacuum'), group: gettext('Options'),
+        schema:[{
+          id: 'vacuum_full', group: gettext('Vacuum'), disabled: 'isDisabled',
+          control: Backform.CustomSwitchControl, label: gettext('FULL'), deps: ['op']
+        },{
+          id: 'vacuum_freeze', deps: ['op'], disabled: 'isDisabled',
+          control: Backform.CustomSwitchControl, label: gettext('FREEZE'), group: gettext('Vacuum')
+        },{
+          id: 'vacuum_analyze', deps: ['op'], disabled: 'isDisabled',
+          control: Backform.CustomSwitchControl, label: gettext('ANALYZE'), group: gettext('Vacuum')
+        }]
+      },
+      {
+        id: 'verbose', group: gettext('Options'), deps: ['op'],
+        control: Backform.CustomSwitchControl, label: gettext('Verbose Messages'), disabled: 'isDisabled'
+      }
+    ],
+
+    // Enable/Disable the items based on the user maintenance operation selection
+    isDisabled: function(m) {
+      var name = this.name,
+        node_info = this.node_info;
+      switch(name) {
+        case 'vacuum_full':
+        case 'vacuum_freeze':
+        case 'vacuum_analyze':
+          if (m.get('op') != 'VACUUM') {
+            return true;
+          }
+          else {
+            return false;
+          }
+          break;
+        case 'verbose':
+          if ('primary_key' in node_info || 'unique_constraint' in node_info ||
+                'index' in node_info ) {
+            if (m.get('op') == 'REINDEX') {
+              setTimeout(function() { m.set('verbose', false); }, 10);
+              return true;
+            }
+          }
+          if (m.get('op') == 'REINDEX') {
+            return true;
+          }
+          else {
+            return false;
+          }
+          break;
+        default:
+          return false;
+      }
+      return false;
+    }
+  });
+
+  pgTools.maintenance = {
+      init: function() {
+
+        // We do not want to initialize the module multiple times.
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        var maintenance_supported_nodes = [
+              'database', 'table', 'primary_key',
+              'unique_constraint', 'index', 'partition'
+            ];
+
+        /**
+         Enable/disable Maintenance menu in tools based on node selected.
+         Maintenance menu will be enabled only when user select table and database node.
+        */
+        var menu_enabled = function(itemData, item, data) {
+         var t = pgBrowser.tree, i = item, d = itemData;
+         var parent_item = t.hasParent(i) ? t.parent(i): null,
+             parent_data = parent_item ? t.itemData(parent_item) : null;
+           if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
+             if (_.indexOf(maintenance_supported_nodes, d._type) !== -1 &&
+               parent_data._type != 'catalog') {
+               if (d._type == 'database' && d.allowConn)
+                 return true;
+               else if(d._type != 'database')
+                 return true;
+               else
+                 return false;
+              }
+              else
+                return false;
+           }
+           else
+             return false;
+        };
+
+        var menus = [{
+          name: 'maintenance', module: this,
+          applies: ['tools'], callback: 'callback_maintenance',
+          priority: 10, label: gettext('Maintenance...'),
+          icon: 'fa fa-wrench', enable: menu_enabled
+        }];
+
+        // Add supported menus into the menus list
+        for (var idx = 0; idx < maintenance_supported_nodes.length; idx++) {
+          menus.push({
+            name: 'maintenance_context_' + maintenance_supported_nodes[idx],
+            node: maintenance_supported_nodes[idx], module: this,
+            applies: ['context'], callback: 'callback_maintenance',
+            priority: 10, label: gettext('Maintenance...'),
+            icon: 'fa fa-wrench', enable: menu_enabled
+          });
+        }
+        pgBrowser.add_menus(menus);
+      },
+
+      /*
+        Open the dialog for the maintenance functionality
+      */
+      callback_maintenance: function(args, item) {
+        var i = item || pgBrowser.tree.selected(),
+          server_data = null;
+
+        while (i) {
+          var node_data = pgBrowser.tree.itemData(i);
+          if (node_data._type == 'server') {
+            server_data = node_data;
+            break;
+          }
+
+          if (pgBrowser.tree.hasParent(i)) {
+            i = $(pgBrowser.tree.parent(i));
+          } else {
+            Alertify.alert(gettext("Please select server or child node from tree."));
+            break;
+          }
+        }
+
+        if (!server_data) {
+          return;
+        }
+
+        var module = 'paths',
+          preference_name = 'pg_bin_dir',
+          msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+        if ((server_data.type && server_data.type == 'ppas') ||
+            server_data.server_type == 'ppas') {
+          preference_name = 'ppas_bin_dir';
+          msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+        }
+
+        var preference = pgBrowser.get_preference(module, preference_name);
+
+        if(preference) {
+          if (!preference.value) {
+            Alertify.alert(gettext('Configuration required'), msg);
+            return;
+          }
+        } else {
+          Alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
+          return;
+        }
+
+        var self = this,
+          input = args || {},
+          t = pgBrowser.tree,
+          i = item || t.selected(),
+          d = i && i.length == 1 ? t.itemData(i) : undefined,
+          node = d && pgBrowser.Nodes[d._type];
+
+        if (!d)
+          return;
+
+        var objName = d.label;
+        var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+        if (!Alertify.MaintenanceDialog) {
+          Alertify.dialog('MaintenanceDialog', function factory() {
+
+            return {
+              main: function(title) {
+                this.set('title', title);
+              },
+              setup: function() {
+                return {
+                  buttons:[{
+                    text: '', className: 'btn btn-default pull-left fa fa-lg fa-info',
+                    attrs:{name:'object_help', type:'button', url: 'maintenance.html', label: gettext('Maintenance')}
+                  },{
+                    text: '', key: 112,
+                    className: 'btn btn-default pull-left fa fa-lg fa-question',
+                    attrs:{
+                      name:'dialog_help', type:'button',
+                      label: gettext('Maintenance'),
+                      url: url_for(
+                        'help.static', {'filename': 'maintenance_dialog.html'}
+                      )
+                    }
+                  },{
+                    text: gettext("OK"), key: 13, className: "btn btn-primary fa fa-lg fa-save pg-alertify-button",
+                    'data-btn-name': 'ok',
+                  },{
+                    text: gettext("Cancel"), key: 27, className: "btn btn-danger fa fa-lg fa-times pg-alertify-button",
+                    'data-btn-name': 'cancel',
+                  }],
+                  options: { modal: 0, pinnable: false}
+                };
+              },
+              // Callback functions when click on the buttons of the Alertify dialogs
+              callback: function(e) {
+                var i = pgBrowser.tree.selected(),
+                  d = i && i.length == 1 ? pgBrowser.tree.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (e.button.element.name == "dialog_help" || e.button.element.name == "object_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    node, i, e.button.element.getAttribute('label'));
+                  return;
+                }
+
+                if (e.button['data-btn-name'] === "ok") {
+
+                  var schema = undefined,
+                    table = undefined,
+                    primary_key = undefined,
+                    unique_constraint = undefined,
+                    index = undefined;
+
+                  if (!d)
+                    return;
+
+                  var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                  if (treeInfo.schema != undefined) {
+                    schema = treeInfo.schema._label;
+                  }
+
+                  if (treeInfo.partition != undefined) {
+                    table = treeInfo.partition._label;
+                  } else if  (treeInfo.table != undefined) {
+                    table = treeInfo.table._label;
+                  }
+
+                  if (treeInfo.primary_key != undefined) {
+                    primary_key = treeInfo.primary_key._label;
+                  } else if (treeInfo.unique_constraint != undefined) {
+                    unique_constraint = treeInfo.unique_constraint._label;
+                  } else if (treeInfo.index != undefined) {
+                    index = treeInfo.index._label;
+                  }
+
+                  this.view.model.set({'database': treeInfo.database._label,
+                                      'schema': schema,
+                                      'table': table,
+                                      'primary_key': primary_key,
+                                      'unique_constraint': unique_constraint,
+                                      'index': index})
+
+                  $.ajax({
+                    url: url_for(
+                      'maintenance.create_job', {
+                        'sid': treeInfo.server._id,
+                        'did': treeInfo.database._id
+                      }),
+                    method: 'POST',
+                    data:{'data': JSON.stringify(this.view.model.toJSON())},
+                    success: function(res) {
+                      if (res.data && res.data.status) {
+                        //Do nothing as we are creating the job and exiting from the main dialog
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.success(res.data.info);
+                        pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+                      }
+                      else {
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.error(res.data.errmsg);
+                      }
+                    },
+                    error: function(e) {
+                      Alertify.alert(
+                        gettext("Maintenance job creation failed.")
+                      );
+                    }
+                  });
+                }
+              },
+              build: function() {
+                Alertify.pgDialogBuild.apply(this)
+              },
+              hooks: {
+                onclose: function() {
+                  if (this.view) {
+                    this.view.remove({data: true, internal: true, silent: true});
+                  }
+                }
+              },
+              prepare: function() {
+                // Main maintenance tool dialog container
+                var $container = $("<div class='maintenance_dlg'></div>");
+
+                var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (!d)
+                  return;
+
+                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                var newModel = new MaintenanceModel (
+                  {}, {node_info: treeInfo}
+                  ),
+                  fields = Backform.generateViewSchema(
+                    treeInfo, newModel, 'create', node, treeInfo.server, true
+                  );
+
+                  var view = this.view = new Backform.Dialog({
+                    el: $container, model: newModel, schema: fields
+                  });
+
+                  $(this.elements.body.childNodes[0]).addClass('alertify_tools_dialog_properties obj_properties');
+                  view.render();
+
+                 // If node is Index, Unique or Primary key then disable vacuum & analyze button
+                  if (d._type == 'primary_key' || d._type == 'unique_constraint'
+                                               || d._type == 'index') {
+                    var vacuum_analyze_btns = $container.find(
+                                                '.pgadmin-controls label:lt(2)'
+                                                ).removeClass('active').addClass('disabled');
+                    // Find reindex button element & add active class to it
+                    var reindex_btn = vacuum_analyze_btns[1].nextElementSibling;
+                    $(reindex_btn).addClass('active');
+                  }
+
+                  this.elements.content.appendChild($container.get(0));
+              }
+            };
+          });
+        }
+
+        // Open the Alertify dialog
+        Alertify.MaintenanceDialog('Maintenance...').set('resizable',true).resizeTo('60%','80%');
+      },
+    };
+
+    return pgAdmin.Tools.maintenance;
+  });
diff --git a/web/pgadmin/tools/restore/__init__.py b/web/pgadmin/tools/restore/__init__.py
index d34d282a..e9189381 100644
--- a/web/pgadmin/tools/restore/__init__.py
+++ b/web/pgadmin/tools/restore/__init__.py
@@ -47,8 +47,9 @@ class RestoreModule(PgAdminModule):
         """
         return [{
             'name': 'pgadmin.tools.restore',
-            'path': url_for('restore.index') + 'restore',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/restore'),
+            'when': None,
+            'is_template': False
         }]
 
     def get_exposed_url_endpoints(self):
@@ -59,9 +60,7 @@ class RestoreModule(PgAdminModule):
         return ['restore.create_job']
 
 # Create blueprint for RestoreModule class
-blueprint = RestoreModule(
-    MODULE_NAME, __name__, static_url_path=''
-)
+blueprint = RestoreModule(MODULE_NAME, __name__, static_url_path='')
 
 
 class RestoreMessage(IProcessDesc):
diff --git a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js
new file mode 100644
index 00000000..bacfcd3f
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore.js
@@ -0,0 +1,563 @@
+// Restore dialog
+define('tools.restore', [
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'underscore.string', 'alertify', 'pgadmin.browser', 'backbone', 'backgrid',
+  'backform', 'pgadmin.browser.node',
+  'sources/alerts/alertify_wrapper',
+], function(
+  gettext, url_for, $, _, S, alertify, pgBrowser, Backbone, Backgrid, Backform,
+  pgNode, AlertifyWrapper
+) {
+
+    // if module is already initialized, refer to that.
+    if (pgBrowser.Restore) {
+      return pgBrowser.Restore;
+    }
+
+    var CustomSwitchControl = Backform.CustomSwitchControl = Backform.SwitchControl.extend({
+        template: _.template([
+          '<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
+          '<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
+          '  <div class="checkbox">',
+          '    <label>',
+          '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+          '    </label>',
+          '  </div>',
+          '</div>',
+          '<% if (helpMessage && helpMessage.length) { %>',
+          '  <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+          '<% } %>'
+        ].join("\n")),
+        className: 'pgadmin-control-group form-group pg-el-xs-4'
+    });
+
+    //Restore Model (Objects like Database/Schema/Table)
+    var RestoreObjectModel = Backbone.Model.extend({
+      idAttribute: 'id',
+      defaults: {
+        custom: false,
+        file: undefined,
+        role: undefined,
+        format: 'custom',
+        verbose: true,
+        blobs: true,
+        encoding: undefined,
+        database: undefined,
+        schemas: undefined,
+        tables: undefined,
+        functions: undefined,
+        triggers: undefined,
+        trigger_funcs: undefined,
+        indexes: undefined
+      },
+
+      // Default values!
+      initialize: function(attrs, args) {
+        // Set default options according to node type selection by user
+        var node_type = attrs.node_data.type;
+
+        if (node_type) {
+          // Only_Schema option
+          if (node_type === 'function' || node_type === 'index'
+          || node_type === 'trigger') {
+            this.set({'only_schema': true}, {silent: true});
+          }
+
+          // Only_Data option
+          if (node_type === 'table') {
+            this.set({'only_data': true}, {silent: true});
+          }
+
+          // Clean option
+          if (node_type === 'function' || node_type === 'trigger_function') {
+            this.set({'clean': true}, {silent: true});
+          }
+        }
+        Backbone.Model.prototype.initialize.apply(this, arguments);
+      },
+      schema: [{
+        id: 'format', label: gettext('Format'),
+        type: 'text', disabled: false,
+        control: 'select2', select2: {
+            allowClear: false,
+            width: "100%"
+        },
+        options: [
+          {label: gettext('Custom or tar'), value: "custom"},
+          {label: gettext('Directory'), value: "directory"}
+        ]
+      },{
+        id: 'file', label: gettext('Filename'),
+        type: 'text', disabled: false, control: Backform.FileControl,
+        dialog_type: 'select_file', supp_types: ['*', 'backup','sql', 'patch']
+      },{
+        id: 'no_of_jobs', label: gettext('Number of jobs'),
+        type: 'int'
+      },{
+        id: 'role', label: gettext('Role name'),
+        control: 'node-list-by-name', node: 'role',
+        select2: { allowClear: false }
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Sections'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'pre_data', label: gettext('Pre-data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return this.node.type !== 'function' && this.node.type !== 'table'
+                   && this.node.type !== 'trigger'
+                   && this.node.type !== 'trigger_function'
+                   && (m.get('only_data') || m.get('only_schema'));
+          }
+        },{
+          id: 'data', label: gettext('Data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return this.node.type !== 'function' && this.node.type !== 'table'
+                   && this.node.type !== 'trigger'
+                   && this.node.type !== 'trigger_function'
+                   && (m.get('only_data') || m.get('only_schema'));
+          }
+        },{
+          id: 'post_data', label: gettext('Post-data'),
+          control: Backform.CustomSwitchControl, group: gettext('Sections'),
+          deps: ['only_data', 'only_schema'], disabled: function(m) {
+            return this.node.type !== 'function' && this.node.type !== 'table'
+                   && this.node.type !== 'trigger'
+                   && this.node.type !== 'trigger_function'
+                   && (m.get('only_data') || m.get('only_schema'));
+          }
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Type of objects'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'only_data', label: gettext('Only data'),
+          control: Backform.CustomSwitchControl, group: gettext('Type of objects'),
+          deps: ['pre_data', 'data', 'post_data','only_schema'], disabled: function(m) {
+            return (this.node.type !== 'database' && this.node.type !== 'schema')
+                   || ( m.get('pre_data')
+                        ||m.get('data')
+                        || m.get('post_data')
+                        || m.get('only_schema')
+                      );
+          }
+        },{
+          id: 'only_schema', label: gettext('Only schema'),
+          control: Backform.CustomSwitchControl, group: gettext('Type of objects'),
+          deps: ['pre_data', 'data', 'post_data', 'only_data'], disabled: function(m) {
+            return (this.node.type !== 'database' && this.node.type !== 'schema')
+                   || ( m.get('pre_data')
+                        || m.get('data')
+                        || m.get('post_data')
+                        || m.get('only_data')
+                      );
+          }
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Do not save'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'dns_owner', label: gettext('Owner'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        },{
+          id: 'dns_privilege', label: gettext('Privilege'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        },{
+          id: 'dns_tablespace', label: gettext('Tablespace'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Do not save')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Queries'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'include_create_database', label: gettext('Include CREATE DATABASE statement'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        },{
+          id: 'clean', label: gettext('Clean before restore'),
+          control: Backform.CustomSwitchControl, group: gettext('Queries'),
+          disabled: function(m) {
+            return this.node.type === 'function' ||
+                     this.node.type === 'trigger_function';
+          }
+        },{
+          id: 'single_transaction', label: gettext('Single transaction'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Queries')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Disable'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'disable_trigger', label: gettext('Trigger'),
+          control: Backform.CustomSwitchControl, group: gettext('Disable')
+        },{
+          id: 'no_data_fail_table', label: gettext('No data for Failed Tables'),
+          control: Backform.CustomSwitchControl, disabled: false, group: gettext('Disable')
+        }]
+      },{
+        type: 'nested', control: 'fieldset', label: gettext('Miscellaneous / Behavior'),
+        group: gettext('Restore options'),
+        schema:[{
+          id: 'verbose', label: gettext('Verbose messages'),
+          control: Backform.CustomSwitchControl, disabled: false,
+          group: gettext('Miscellaneous / Behavior')
+        },{
+          id: 'use_set_session_auth', label: gettext('Use SET SESSION AUTHORIZATION'),
+          control: Backform.CustomSwitchControl, disabled: false,
+          group: gettext('Miscellaneous / Behavior')
+        },{
+          id: 'exit_on_error', label: gettext('Exit on error'),
+          control: Backform.CustomSwitchControl, disabled: false,
+          group: gettext('Miscellaneous / Behavior')
+        }]
+      }],
+      validate: function() {
+        return null;
+      }
+    });
+
+    // Create an Object Restore of pgBrowser class
+    pgBrowser.Restore  = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        // Define list of nodes on which restore context menu option appears
+        var restore_supported_nodes = [
+              'database', 'schema',
+              'table', 'function',
+              'trigger', 'index',
+              'partition'
+            ];
+
+        /**
+          Enable/disable restore menu in tools based
+          on node selected
+          if selected node is present in supported_nodes,
+          menu will be enabled otherwise disabled.
+          Also, hide it for system view in catalogs
+        */
+        var menu_enabled = function(itemData, item, data) {
+          var t = pgBrowser.tree, i = item, d = itemData;
+          var parent_item = t.hasParent(i) ? t.parent(i): null,
+              parent_data = parent_item ? t.itemData(parent_item) : null;
+            if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
+              if (_.indexOf(restore_supported_nodes, d._type) !== -1 &&
+                is_parent_catalog(itemData, item, data) ) {
+                  if (d._type == 'database' && d.allowConn)
+                    return true;
+                  else if(d._type != 'database')
+                    return true;
+                  else
+                    return false;
+              }
+              else
+                return false;
+            }
+            else
+              return false;
+        };
+
+        var is_parent_catalog = function(itemData, item, data) {
+          var t = pgBrowser.tree, i = item, d = itemData;
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to restore
+            if (_.indexOf(['catalog'], d._type) > -1)
+              return false;
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // by default we do not want to allow create menu
+          return true;
+        }
+
+        // Define the nodes on which the menus to be appear
+        var menus = [{
+          name: 'restore_object', module: this,
+          applies: ['tools'], callback: 'restore_objects',
+          priority: 13, label: gettext('Restore...'),
+          icon: 'fa fa-upload', enable: menu_enabled
+        }];
+
+        for (var idx = 0; idx < restore_supported_nodes.length; idx++) {
+          menus.push({
+            name: 'restore_' + restore_supported_nodes[idx],
+            node: restore_supported_nodes[idx], module: this,
+            applies: ['context'], callback: 'restore_objects',
+            priority: 13, label: gettext('Restore...'),
+            icon: 'fa fa-upload', enable: menu_enabled
+            });
+        }
+
+        pgAdmin.Browser.add_menus(menus);
+        return this;
+      },
+      // Callback to draw Backup Dialog for objects
+      restore_objects: function(action, treeItem) {
+
+        var i = treeItem || pgBrowser.tree.selected(),
+          server_data = null;
+
+        while (i) {
+          var node_data = pgBrowser.tree.itemData(i);
+          if (node_data._type == 'server') {
+            server_data = node_data;
+            break;
+          }
+
+          if (pgBrowser.tree.hasParent(i)) {
+            i = $(pgBrowser.tree.parent(i));
+          } else {
+            alertify.alert(gettext("Please select server or child node from tree."));
+            break;
+          }
+        }
+
+        if (!server_data) {
+          return;
+        }
+
+        var module = 'paths',
+          preference_name = 'pg_bin_dir',
+          msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+        if ((server_data.type && server_data.type == 'ppas') ||
+            server_data.server_type == 'ppas') {
+          preference_name = 'ppas_bin_dir';
+          msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+        }
+
+        var preference = pgBrowser.get_preference(module, preference_name);
+
+        if(preference) {
+          if (!preference.value) {
+            alertify.alert(gettext('Configuration required'), msg);
+            return;
+          }
+        } else {
+          alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
+          return;
+        }
+
+        var title = S(gettext('Restore (%s: %s)')),
+            tree = pgBrowser.tree,
+            item = treeItem || tree.selected(),
+            data = item && item.length == 1 && tree.itemData(item),
+            node = data && data._type && pgBrowser.Nodes[data._type];
+
+        if (!node)
+          return;
+
+        title = title.sprintf(node.label, data.label).value();
+
+        if(!alertify.pg_restore) {
+          // Create Dialog title on the fly with node details
+          alertify.dialog('pg_restore' ,function factory() {
+            return {
+               main: function(title, item, data, node) {
+                this.set('title', title);
+                this.setting('pg_node', node);
+                this.setting('pg_item', item);
+                this.setting('pg_item_data', data);
+               },
+               build: function() {
+                alertify.pgDialogBuild.apply(this)
+               },
+               setup:function() {
+                return {
+                  buttons: [{
+                    text: '', className: 'btn btn-default pull-left fa fa-lg fa-info',
+                    attrs:{name:'object_help', type:'button', url: 'backup.html', label: gettext('Restore')}
+                  },{
+                    text: '', key: 112, className: 'btn btn-default pull-left fa fa-lg fa-question',
+                    attrs:{
+                      name:'dialog_help', type:'button', label: gettext('Restore'),
+                      url: url_for('help.static', {'filename': 'restore_dialog.html'})
+                    }
+                  },{
+                    text: gettext('Restore'), key: 13,
+                    className: 'btn btn-primary fa fa-upload pg-alertify-button', restore: true,
+                    'data-btn-name': 'restore'
+                  },{
+                    text: gettext('Cancel'), key: 27,
+                    className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button', restore: false,
+                    'data-btn-name': 'cancel'
+                  }],
+                  // Set options for dialog
+                  options: {
+                    title: title,
+                    //disable both padding and overflow control.
+                    padding : !1,
+                    overflow: !1,
+                    model: 0,
+                    resizable: true,
+                    maximizable: true,
+                    pinnable: false,
+                    closableByDimmer: false,
+                    modal: false
+                  }
+                };
+              },
+              hooks: {
+                // triggered when the dialog is closed
+                onclose: function() {
+                  if (this.view) {
+                    this.view.remove({data: true, internal: true, silent: true});
+                  }
+                }
+              },
+              settings:{
+                pg_node: null,
+                pg_item: null,
+                pg_item_data: null
+              },
+              prepare: function() {
+
+                var self = this;
+                // Disable Backup button until user provides Filename
+                this.__internal.buttons[2].element.disabled = true;
+                var $container = $("<div class='restore_dialog'></div>");
+                var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  node = d && pgBrowser.Nodes[d._type];
+
+                if (!d)
+                  return;
+
+                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+                var newModel = new RestoreObjectModel(
+                  {node_data: node}, {node_info: treeInfo}
+                  ),
+                  fields = Backform.generateViewSchema(
+                    treeInfo, newModel, 'create', node, treeInfo.server, true
+                  );
+
+                var view = this.view = new Backform.Dialog({
+                  el: $container, model: newModel, schema: fields
+                });
+
+                $(this.elements.body.childNodes[0]).addClass(
+                  'alertify_tools_dialog_properties obj_properties'
+                );
+
+                view.render();
+
+                this.elements.content.appendChild($container.get(0));
+
+                 // Listen to model & if filename is provided then enable Backup button
+                this.view.model.on('change', function() {
+                    if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+                      this.errorModel.clear();
+                      self.__internal.buttons[2].element.disabled = false;
+                    } else {
+                      self.__internal.buttons[2].element.disabled = true;
+                      this.errorModel.set('file', gettext('Please provide filename'))
+                    }
+                });
+
+              },
+              // Callback functions when click on the buttons of the Alertify dialogs
+              callback: function(e) {
+                // Fetch current server id
+                var t = pgBrowser.tree,
+                  i = this.settings['pg_item'] || t.selected(),
+                  d = this.settings['pg_item_data'] || (
+                    i && i.length == 1 ? t.itemData(i) : undefined
+                  ),
+                  node = this.settings['pg_node'] || (
+                    d && pgBrowser.Nodes[d._type]
+                  );
+
+                if (e.button.element.name == "dialog_help" || e.button.element.name == "object_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    node, i, e.button.element.getAttribute('label'));
+                  return;
+                }
+
+                if (e.button['data-btn-name'] === "restore") {
+                  if (!d)
+                    return;
+
+                  var info = node.getTreeNodeHierarchy.apply(node, [i]),
+                      m = this.view.model;
+                  // Set current node info into model
+                  m.set('database', info.database._label);
+                  if (!m.get('custom')) {
+                    switch (d._type) {
+                      case 'schema':
+                        m.set('schemas', [d._label]);
+                        break;
+                      case 'table':
+                        m.set('schemas', [info.schema._label]);
+                        m.set('tables', [d._label]);
+                        break;
+                      case 'function':
+                        m.set('schemas', [info.schema._label]);
+                        m.set('functions', [d._label]);
+                        break;
+                      case 'index':
+                        m.set('schemas', [info.schema._label]);
+                        m.set('indexes', [d._label]);
+                        break;
+                      case 'trigger':
+                        m.set('schemas', [info.schema._label]);
+                        m.set('triggers', [d._label]);
+                        break;
+                      case 'trigger_func':
+                        m.set('schemas', [info.schema._label]);
+                        m.set('trigger_funcs', [d._label]);
+                        break;
+                    }
+                  } else {
+                    // TODO::
+                    // When we will implement the object selection in the
+                    // import dialog, we will need to select the objects from
+                    // the tree selection tab.
+                  }
+
+                  var self = this,
+                      baseUrl = url_for('restore.create_job', {'sid': info.server._id}),
+                      args =  this.view.model.toJSON();
+
+                  $.ajax({
+                    url: baseUrl,
+                    method: 'POST',
+                    data:{ 'data': JSON.stringify(args) },
+                    success: function(res) {
+                      if (res.success) {
+                        var alertifyWrapper = new AlertifyWrapper();
+                        alertifyWrapper.success(
+                          gettext('Restore job created.'), 5
+                        );
+                        pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+                      } else {
+                        console.log(res);
+                      }
+                    },
+                    error: function(xhr, status, error) {
+                      try {
+                        var err = $.parseJSON(xhr.responseText);
+                        alertify.alert(
+                          gettext('Restore failed.'),
+                          err.errormsg
+                        );
+                      } catch (e) {}
+                    }
+                  });
+                }
+             }
+          };
+        });
+      }
+
+      alertify.pg_restore(title, item, data, node).resizeTo('65%','60%');
+      }
+    };
+    return pgBrowser.Restore;
+  });
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 2ffc74a5..3423dd5f 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -73,8 +73,9 @@ class SqlEditorModule(PgAdminModule):
     def get_own_javascripts(self):
         return [{
             'name': 'pgadmin.sqleditor',
-            'path': url_for('sqleditor.index') + "sqleditor",
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/sqleditor'),
+            'when': None,
+            'is_template': False
         }]
 
 
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
new file mode 100644
index 00000000..3134c8df
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -0,0 +1,3799 @@
+define('tools.querytool', [
+    'babel-polyfill', 'sources/gettext','sources/url_for', 'jquery',
+    'underscore', 'underscore.string', 'alertify',
+    'pgadmin', 'backbone', 'sources/../bundle/codemirror',
+    'pgadmin.misc.explain',
+    'sources/selection/grid_selector',
+    'sources/selection/active_cell_capture',
+    'sources/selection/clipboard',
+    'sources/selection/copy_data',
+    'sources/selection/range_selection_helper',
+    'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
+    'sources/selection/xcell_selection_model',
+    'sources/selection/set_staged_rows',
+    'sources/sqleditor_utils',
+    'sources/history/index.js',
+    'sources/../jsx/history/query_history',
+    'react', 'react-dom',
+    'sources/alerts/alertify_wrapper',
+    'sources/sqleditor/keyboard_shortcuts',
+    'sources/../bundle/slickgrid',
+    'pgadmin.file_manager',
+    'backgrid.sizeable.columns',
+    'slick.pgadmin.formatters',
+    'slick.pgadmin.editors',
+    'pgadmin.browser'
+], function(
+  babelPollyfill, gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror,
+  pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+  XCellSelectionModel, setStagedRows,  SqlEditorUtils, HistoryBundle, queryHistory, React, ReactDOM, AlertifyWrapper,
+  keyboardShortcuts
+) {
+    /* Return back, this has been called more than once */
+    if (pgAdmin.SqlEditor)
+      return pgAdmin.SqlEditor;
+
+    // Some scripts do export their object in the window only.
+    // Generally the one, which do no have AMD support.
+    var wcDocker = window.wcDocker,
+        pgBrowser = pgAdmin.Browser,
+        CodeMirror = codemirror.default,
+        Slick = window.Slick;
+
+    var is_query_running = false;
+
+    // Defining Backbone view for the sql grid.
+    var SQLEditorView = Backbone.View.extend({
+      initialize: function(opts) {
+        this.$el = opts.el;
+        this.handler = opts.handler;
+        this.handler['col_size'] = {};
+      },
+
+      // Bind all the events
+      events: {
+        "click .btn-load-file": "on_file_load",
+        "click #btn-save": "on_save",
+        "click #btn-file-menu-save": "on_save",
+        "click #btn-file-menu-save-as": "on_save_as",
+        "click #btn-find": "on_find",
+        "click #btn-find-menu-find": "on_find",
+        "click #btn-find-menu-find-next": "on_find_next",
+        "click #btn-find-menu-find-previous": "on_find_previous",
+        "click #btn-find-menu-replace": "on_replace",
+        "click #btn-find-menu-replace-all": "on_replace_all",
+        "click #btn-find-menu-find-persistent": "on_find_persistent",
+        "click #btn-find-menu-jump": "on_jump",
+        "click #btn-delete-row": "on_delete",
+        "click #btn-filter": "on_show_filter",
+        "click #btn-filter-menu": "on_show_filter",
+        "click #btn-include-filter": "on_include_filter",
+        "click #btn-exclude-filter": "on_exclude_filter",
+        "click #btn-remove-filter": "on_remove_filter",
+        "click #btn-apply": "on_apply",
+        "click #btn-cancel": "on_cancel",
+        "click #btn-copy-row": "on_copy_row",
+        "click #btn-paste-row": "on_paste_row",
+        "click #btn-flash": "on_flash",
+        "click #btn-flash-menu": "on_flash",
+        "click #btn-cancel-query": "on_cancel_query",
+        "click #btn-download": "on_download",
+        "click #btn-edit": "on_clear",
+        "click #btn-clear": "on_clear",
+        "click #btn-auto-commit": "on_auto_commit",
+        "click #btn-auto-rollback": "on_auto_rollback",
+        "click #btn-clear-history": "on_clear_history",
+        "click .noclose": 'do_not_close_menu',
+        "click #btn-explain": "on_explain",
+        "click #btn-explain-analyze": "on_explain_analyze",
+        "click #btn-explain-verbose": "on_explain_verbose",
+        "click #btn-explain-costs": "on_explain_costs",
+        "click #btn-explain-buffers": "on_explain_buffers",
+        "click #btn-explain-timing": "on_explain_timing",
+        "change .limit": "on_limit_change",
+        "keydown": "keyAction",
+        // Comment options
+        "click #btn-comment-code": "on_toggle_comment_block_code",
+        "click #btn-toggle-comment-block": "on_toggle_comment_block_code",
+        "click #btn-comment-line": "on_comment_line_code",
+        "click #btn-uncomment-line": "on_uncomment_line_code",
+        // Indentation options
+        "click #btn-indent-code": "on_indent_code",
+        "click #btn-unindent-code": "on_unindent_code"
+      },
+
+      // This function is used to render the template.
+      render: function() {
+        var self = this,
+          filter = self.$el.find('#sql_filter');
+
+        $('.editor-title').text(_.unescape(self.editor_title));
+        self.filter_obj = CodeMirror.fromTextArea(filter.get(0), {
+            lineNumbers: true,
+            indentUnit: 4,
+            mode: "text/x-pgsql",
+            foldOptions: {
+              widget: "\u2026"
+            },
+            foldGutter: {
+              rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
+                                CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
+            },
+            gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
+            extraKeys: pgBrowser.editor_shortcut_keys,
+            tabSize: pgAdmin.Browser.editor_options.tabSize,
+            lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
+            autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
+            matchBrackets: pgAdmin.Browser.editor_options.brace_matching
+        });
+
+        // Create main wcDocker instance
+        var main_docker = new wcDocker(
+          '#editor-panel', {
+          allowContextMenu: false,
+          allowCollapse: false,
+          themePath: url_for('static', {'filename': 'css'}),
+          theme: 'webcabin.overrides.css'
+        });
+
+        var sql_panel = new pgAdmin.Browser.Panel({
+          name: 'sql_panel',
+          title: false,
+          width: '100%',
+          height:'20%',
+          isCloseable: false,
+          isPrivate: true
+        });
+
+        sql_panel.load(main_docker);
+        var sql_panel_obj = main_docker.addPanel('sql_panel', wcDocker.DOCK.TOP);
+
+        var text_container = $('<textarea id="sql_query_tool"></textarea>');
+        var output_container = $('<div id="output-panel"></div>').append(text_container);
+        sql_panel_obj.$container.find('.pg-panel-content').append(output_container);
+
+        self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), {
+            lineNumbers: true,
+            indentUnit: 4,
+            styleSelectedText: true,
+            mode: "text/x-pgsql",
+            foldOptions: {
+              widget: "\u2026"
+            },
+            foldGutter: {
+              rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
+                                CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
+            },
+            gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
+            extraKeys: pgBrowser.editor_shortcut_keys,
+            tabSize: pgAdmin.Browser.editor_options.tabSize,
+            lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
+            scrollbarStyle: 'simple',
+            autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
+            matchBrackets: pgAdmin.Browser.editor_options.brace_matching
+        });
+
+        // Refresh Code mirror on SQL panel resize to
+        // display its value properly
+        sql_panel_obj.on(wcDocker.EVENT.RESIZE_ENDED, function() {
+          setTimeout(function() {
+            if(self && self.query_tool_obj) {
+              self.query_tool_obj.refresh();
+            }
+          }, 200);
+        });
+
+        // Create panels for 'Data Output', 'Explain', 'Messages' and 'History'
+        var data_output = new pgAdmin.Browser.Panel({
+          name: 'data_output',
+          title: gettext("Data Output"),
+          width: '100%',
+          height:'100%',
+          isCloseable: false,
+          isPrivate: true,
+          content: '<div id ="datagrid" class="sql-editor-grid-container text-12"></div>'
+        })
+
+        var explain = new pgAdmin.Browser.Panel({
+          name: 'explain',
+          title: gettext("Explain"),
+          width: '100%',
+          height:'100%',
+          isCloseable: false,
+          isPrivate: true,
+          content: '<div class="sql-editor-explain"></div>'
+        })
+
+        var messages = new pgAdmin.Browser.Panel({
+          name: 'messages',
+          title: gettext("Messages"),
+          width: '100%',
+          height:'100%',
+          isCloseable: false,
+          isPrivate: true,
+          content: '<div class="sql-editor-message"></div>'
+        })
+
+        var history = new pgAdmin.Browser.Panel({
+          name: 'history',
+          title: gettext("History"),
+          width: '100%',
+          height:'100%',
+          isCloseable: false,
+          isPrivate: true,
+          content: '<div id ="history_grid" class="sql-editor-history-container"></div>'
+        })
+
+        // Load all the created panels
+        data_output.load(main_docker);
+        explain.load(main_docker);
+        messages.load(main_docker);
+        history.load(main_docker);
+
+        // Add all the panels to the docker
+        self.data_output_panel = main_docker.addPanel('data_output', wcDocker.DOCK.BOTTOM, sql_panel_obj);
+        self.explain_panel = main_docker.addPanel('explain', wcDocker.DOCK.STACKED, self.data_output_panel);
+        self.messages_panel = main_docker.addPanel('messages', wcDocker.DOCK.STACKED, self.data_output_panel);
+        self.history_panel = main_docker.addPanel('history', wcDocker.DOCK.STACKED, self.data_output_panel);
+
+        self.render_history_grid();
+
+        if (!self.handler.is_new_browser_tab) {
+          // Listen on the panel closed event and notify user to save modifications.
+          _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
+            if(p.isVisible()) {
+              p.on(wcDocker.EVENT.CLOSING, function() {
+                // Only if we can edit data then perform this check
+                var notify = false, msg;
+                if(self.handler.can_edit) {
+                  var data_store = self.handler.data_store;
+                  if(data_store && (_.size(data_store.added) ||
+                      _.size(data_store.updated))) {
+                    msg = gettext("The data has changed. Do you want to save changes?");
+                    notify = true;
+                  }
+                } else if(self.handler.is_query_tool && self.handler.is_query_changed) {
+                  msg = gettext("The text has changed. Do you want to save changes?");
+                  notify = true;
+                }
+                if(notify) {return self.user_confirmation(p, msg);}
+                return true;
+              });
+              // Set focus on query tool of active panel
+              p.on(wcDocker.EVENT.GAIN_FOCUS, function() {
+                if (!$(p.$container).hasClass('wcPanelTabContentHidden')) {
+                  setTimeout(function() {
+                    self.handler.gridView.query_tool_obj.focus();
+                  }, 200);
+                }
+              });
+            }
+          });
+        }
+
+        // set focus on query tool once loaded
+        setTimeout(function() {
+          self.query_tool_obj.focus();
+        }, 500);
+
+        /* We have override/register the hint function of CodeMirror
+         * to provide our own hint logic.
+         */
+        CodeMirror.registerHelper("hint", "sql", function(editor, options) {
+          var data = [],
+              doc = editor.getDoc(),
+              cur = doc.getCursor(),
+              // Get the current cursor position
+              current_cur = cur.ch,
+              // function context
+              ctx = {
+                editor: editor,
+                // URL for auto-complete
+                url: url_for('sqleditor.autocomplete', {'trans_id': self.transId}),
+                data: data,
+                // Get the line number in the cursor position
+                current_line: cur.line,
+                /*
+                 * Render function for hint to add our own class
+                 * and icon as per the object type.
+                 */
+                hint_render: function(elt, data, cur) {
+                  var el = document.createElement('span');
+
+                  switch(cur.type) {
+                  case 'database':
+                      el.className = 'sqleditor-hint pg-icon-' + cur.type;
+                      break;
+                      case 'datatype':
+                      el.className = 'sqleditor-hint icon-type';
+                      break;
+                      case 'keyword':
+                      el.className = 'fa fa-key';
+                      break;
+                      case 'table alias':
+                      el.className = 'fa fa-at';
+                      break;
+                      default:
+                      el.className = 'sqleditor-hint icon-' + cur.type;
+                  }
+
+                  el.appendChild(document.createTextNode(cur.text));
+                  elt.appendChild(el);
+                }
+              };
+
+          data.push(doc.getValue());
+          // Get the text from start to the current cursor position.
+          data.push(
+            doc.getRange(
+              { line: 0, ch: 0 },
+              { line: ctx.current_line, ch: current_cur }
+            )
+          );
+
+          return {
+            then: function(cb) {
+              var self = this;
+              // Make ajax call to find the autocomplete data
+              $.ajax({
+                url: self.url,
+                method: 'POST',
+                contentType: "application/json",
+                data: JSON.stringify(self.data),
+                success: function(res) {
+                  var result = [];
+
+                  _.each(res.data.result, function(obj, key) {
+                    result.push({
+                      text: key, type: obj.object_type,
+                      render: self.hint_render
+                    });
+                  });
+
+                  // Sort function to sort the suggestion's alphabetically.
+                  result.sort(function(a, b){
+                    var textA = a.text.toLowerCase(), textB = b.text.toLowerCase();
+                    if (textA < textB) //sort string ascending
+                      return -1;
+                    if (textA > textB)
+                      return 1;
+                    return 0; //default return value (no sorting)
+                  });
+
+                  /*
+                   * Below logic find the start and end point
+                   * to replace the selected auto complete suggestion.
+                   */
+                  var token = self.editor.getTokenAt(cur), start, end, search;
+                  if (token.end > cur.ch) {
+                    token.end = cur.ch;
+                    token.string = token.string.slice(0, cur.ch - token.start);
+                  }
+
+                  if (token.string.match(/^[.`\w@]\w*$/)) {
+                    search = token.string;
+                    start = token.start;
+                    end = token.end;
+                  } else {
+                    start = end = cur.ch;
+                    search = "";
+                  }
+
+                  /*
+                   * Added 1 in the start position if search string
+                   * started with "." or "`" else auto complete of code mirror
+                   * will remove the "." when user select any suggestion.
+                   */
+                  if (search.charAt(0) == "." || search.charAt(0) == "``")
+                    start += 1;
+
+                  cb({
+                    list: result,
+                    from: {line: self.current_line, ch: start },
+                    to: { line: self.current_line, ch: end }
+                  });
+                }
+              });
+            }.bind(ctx)
+          };
+        });
+      },
+
+      /* To prompt user for unsaved changes */
+      user_confirmation: function(panel, msg) {
+        // If there is anything to save then prompt user
+        var that = this;
+
+        alertify.confirmSave || alertify.dialog('confirmSave', function() {
+          return {
+            main: function(title, message) {
+                var content = '<div class="ajs-content">'
+                + gettext('The text has changed. Do you want to save changes?')
+                + '</div>';
+              this.setHeader(title);
+              this.setContent(message);
+            },
+            setup: function () {
+              return {
+                buttons: [
+                    {
+                      text: gettext('Save'),
+                      className: 'btn btn-primary',
+                    },{
+                      text: gettext('Don\'t save'),
+                      className: 'btn btn-danger',
+                    },{
+                      text: gettext('Cancel'),
+                      key: 27, // ESC
+                      invokeOnClose: true,
+                      className: 'btn btn-warning',
+                    }
+                ],
+                focus: {
+                      element: 0,
+                      select: false
+                },
+                options: {
+                    maximizable: false,
+                    resizable: false
+                }
+              };
+            },
+            callback: function (closeEvent) {
+              switch (closeEvent.index) {
+                case 0: // Save
+                  that.handler.close_on_save = true;
+                  that.handler._save(that, that.handler);
+                  break;
+                case 1: // Don't Save
+                  that.handler.close_on_save = false;
+                  that.handler.close();
+                  break;
+                case 2: //Cancel
+                  //Do nothing.
+                  break;
+              }
+            }
+          };
+        });
+        alertify.confirmSave(gettext("Save changes?"), msg);
+        return false;
+      },
+
+      /* Regarding SlickGrid usage in render_grid function.
+
+       SlickGrid Plugins:
+       ------------------
+       1) Slick.AutoTooltips
+         - This plugin is useful for displaying cell data as tooltip when
+            user hover mouse on cell if data is large
+       2) Slick.CheckboxSelectColumn
+         - This plugin is useful for selecting rows using checkbox
+       3) RowSelectionModel
+         - This plugin is needed by CheckboxSelectColumn plugin to select rows
+
+       Grid Options:
+       -------------
+       1) editable
+         - This option allow us to make grid editable
+       2) enableAddRow
+         - This option allow us to add new rows at the end of grid
+       3) enableCellNavigation
+         - This option allow us to navigate cells using keyboard
+       4) enableColumnReorder
+         - This option allow us to record column
+       5) asyncEditorLoading
+         - This option allow us to open editor async
+       6) autoEdit
+         - This option allow us to enter in edit mode directly when user clicks on it
+           otherwise user have to double click or manually press enter on cell to go
+           in cell edit mode
+
+       Handling of data:
+       -----------------
+         We are doing data handling manually,what user adds/updates/deletes etc
+         we will use `data_store` object to store everything user does within grid data
+
+         - updated:
+           This will hold all the data which user updates in grid
+         - added:
+           This will hold all the new row(s) data which user adds in grid
+         - staged_rows:
+           This will hold all the data which user copies/pastes/deletes in grid
+         - deleted:
+           This will hold all the data which user deletes in grid
+
+       Events handling:
+       ----------------
+         1) onCellChange
+           - We are using this event to listen to changes on individual cell.
+         2) onAddNewRow
+           - We are using this event to listen to new row adding functionality.
+         3) onSelectedRangesChanged
+           - We are using this event to listen when user selects rows for copy/delete operation.
+         4) onBeforeEditCell
+           - We are using this event to save the data before users modified them
+         5) onKeyDown
+           - We are using this event for Copy operation on grid
+       */
+
+      // This function is responsible to create and render the SlickGrid.
+      render_grid: function(collection, columns, is_editable, client_primary_key, rows_affected) {
+        var self = this;
+
+        // This will work as data store and holds all the
+        // inserted/updated/deleted data from grid
+        self.handler.data_store = {
+          updated: {},
+          added: {},
+          staged_rows: {},
+          deleted: {},
+          updated_index: {},
+          added_index: {}
+        };
+
+        // To store primary keys before they gets changed
+        self.handler.primary_keys_data = {};
+
+        self.client_primary_key = client_primary_key;
+
+        self.client_primary_key_counter = 0;
+
+        // Remove any existing grid first
+        if (self.handler.slickgrid) {
+            self.handler.slickgrid.destroy();
+        }
+
+        if(!_.isArray(collection) || !_.size(collection)) {
+          collection = [];
+        }
+
+        var grid_columns = [],
+          table_name;
+        var column_size = self.handler['col_size'],
+          query = self.handler.query,
+          // Extract table name from query
+          table_list = query.match(/select.*from\s+(\w+)/i);
+
+        if (!table_list) {
+          table_name = SqlEditorUtils.getHash(query);
+        }
+        else {
+          table_name = table_list[1];
+        }
+
+        self.handler['table_name'] = table_name;
+        column_size[table_name] = column_size[table_name] || {};
+
+        var grid_width = $($('#editor-panel').find('.wcFrame')[1]).width();
+        _.each(columns, function(c) {
+            var options = {
+              id: c.name,
+              pos: c.pos,
+              field: c.name,
+              name: c.label,
+              display_name: c.display_name,
+              column_type: c.column_type,
+              column_type_internal: c.column_type_internal,
+              not_null: c.not_null,
+              has_default_val: c.has_default_val
+            };
+
+            // Get the columns width based on longer string among data type or
+            // column name.
+            var column_type = c.column_type.trim();
+            var label = c.name.length > column_type.length ? c.name : column_type;
+
+            if (_.isUndefined(column_size[table_name][c.name])) {
+              options['width'] = SqlEditorUtils.calculateColumnWidth(label);
+              column_size[table_name][c.name] = options['width'];
+            }
+            else {
+              options['width'] = column_size[table_name][c.name];
+            }
+
+            // If grid is editable then add editor else make it readonly
+            if(c.cell == 'Json') {
+              options['editor'] = is_editable ? Slick.Editors.JsonText
+                                              : Slick.Editors.ReadOnlyJsonText;
+              options['formatter'] = Slick.Formatters.JsonString;
+            } else if(c.cell == 'number' ||
+              $.inArray(c.type, ['oid', 'xid', 'real']) !== -1
+            ) {
+              options['editor'] = is_editable ? Slick.Editors.CustomNumber
+                                              : Slick.Editors.ReadOnlyText;
+              options['formatter'] = Slick.Formatters.Numbers;
+            } else if(c.cell == 'boolean') {
+              options['editor'] = is_editable ? Slick.Editors.Checkbox
+                                              : Slick.Editors.ReadOnlyCheckbox;
+              options['formatter'] = Slick.Formatters.Checkmark;
+            } else {
+              options['editor'] = is_editable ? Slick.Editors.pgText
+                                                : Slick.Editors.ReadOnlypgText;
+              options['formatter'] = Slick.Formatters.Text;
+            }
+
+           grid_columns.push(options)
+        });
+
+        var gridSelector = new GridSelector();
+        grid_columns = self.grid_columns = gridSelector.getColumnDefinitions(grid_columns);
+
+        if (rows_affected) {
+          // calculate with for header row column.
+        grid_columns[0]['width'] = SqlEditorUtils.calculateColumnWidth(rows_affected);
+        }
+
+        var grid_options = {
+          editable: true,
+          enableAddRow: is_editable,
+          enableCellNavigation: true,
+          enableColumnReorder: false,
+          asyncEditorLoading: false,
+          autoEdit: false
+        };
+
+        var $data_grid = self.$el.find('#datagrid');
+        // Calculate height based on panel size at runtime & set it
+        var grid_height = $($('#editor-panel').find('.wcFrame')[1]).height() - 35;
+        $data_grid.height(grid_height);
+
+        var dataView = self.dataView = new Slick.Data.DataView(),
+            grid = self.grid = new Slick.Grid($data_grid, dataView, grid_columns, grid_options);
+
+        // Add-on function which allow us to identify the faulty row after insert/update
+        // and apply css accordingly
+
+        dataView.getItemMetadata = function(i) {
+          var res = {}, cssClass = '',
+              data_store = self.handler.data_store;
+
+          if (_.has(self.handler, 'data_store')) {
+            if (i in data_store.added_index &&
+              data_store.added_index[i] in data_store.added) {
+              cssClass = 'new_row';
+              if (data_store.added[data_store.added_index[i]].err) {
+                cssClass += ' error';
+              }
+            } else if (i in data_store.updated_index && i in data_store.updated) {
+              cssClass = 'updated_row';
+              if (data_store.updated[data_store.updated_index[i]].err) {
+                cssClass += ' error';
+              }
+            }
+          }
+          // Disable rows having default values
+          if (!_.isUndefined(self.handler.rows_to_disable) &&
+            _.indexOf(self.handler.rows_to_disable, i) !== -1
+          ) {
+            cssClass += ' disabled_row';
+          }
+          return {'cssClasses': cssClass};
+        };
+
+        grid.registerPlugin( new Slick.AutoTooltips({ enableForHeaderCells: false }) );
+        grid.registerPlugin(new ActiveCellCapture());
+        grid.setSelectionModel(new XCellSelectionModel());
+        grid.registerPlugin(gridSelector);
+
+        var editor_data = {
+          keys: self.handler.primary_keys,
+          vals: collection,
+          columns: columns,
+          grid: grid,
+          selection: grid.getSelectionModel(),
+          editor: self,
+          client_primary_key: self.client_primary_key
+        };
+
+        self.handler.slickgrid = grid;
+
+        // Listener function to watch selected rows from grid
+        if (editor_data.selection) {
+            editor_data.selection.onSelectedRangesChanged.subscribe(
+                setStagedRows.bind(editor_data));
+        }
+
+        grid.onColumnsResized.subscribe(function (e, args) {
+            var columns = this.getColumns();
+            _.each(columns, function(col, key) {
+                var column_size = self.handler['col_size'];
+                column_size[self.handler['table_name']][col['id']] = col['width'];
+            });
+        });
+
+        gridSelector.onBeforeGridSelectAll.subscribe(function(e, args) {
+          if (self.handler.has_more_rows) {
+            // this will prevent selection un-till we load all data
+            e.stopImmediatePropagation();
+            self.fetch_next_all(function() {
+              // since we've stopped event propagation we need to
+              // trigger onGridSelectAll manually with new event data.
+              gridSelector.onGridSelectAll.notify(args, new Slick.EventData());
+            });
+          }
+        });
+
+        gridSelector.onBeforeGridColumnSelectAll.subscribe(function(e, args) {
+          if (self.handler.has_more_rows) {
+            // this will prevent selection un-till we load all data
+            e.stopImmediatePropagation();
+            self.fetch_next_all(function() {
+              // since we've stopped event propagation we need to
+              // trigger onGridColumnSelectAll manually with new event data.
+              gridSelector.onGridColumnSelectAll.notify(args, new Slick.EventData());
+            });
+          }
+        });
+
+        // listen for row count change.
+        dataView.onRowCountChanged.subscribe(function (e, args) {
+          grid.updateRowCount();
+          grid.render();
+        });
+
+        // listen for rows change.
+        dataView.onRowsChanged.subscribe(function (e, args) {
+          grid.invalidateRows(args.rows);
+          grid.render();
+        });
+
+        // Listener function which will be called before user updates existing cell
+        // This will be used to collect primary key for that row
+        grid.onBeforeEditCell.subscribe(function (e, args) {
+            if (args.column.column_type_internal == 'bytea' ||
+                args.column.column_type_internal == 'bytea[]') {
+              return false;
+            }
+
+            var before_data = args.item;
+
+            // If newly added row is saved but grid is not refreshed,
+            // then disable cell editing for that row
+            if(self.handler.rows_to_disable &&
+              _.contains(self.handler.rows_to_disable, args.row)) {
+              return false;
+            }
+
+            if(self.handler.can_edit && before_data && self.client_primary_key in before_data) {
+              var _pk = before_data[self.client_primary_key],
+                _keys = self.handler.primary_keys,
+                current_pk = {}, each_pk_key = {};
+
+              // If already have primary key data then no need to go ahead
+              if(_pk in self.handler.primary_keys_data) {
+                return;
+              }
+
+              // Fetch primary keys for the row before they gets modified
+              var _columns = self.handler.columns;
+              _.each(_keys, function(value, key) {
+                current_pk[key] = before_data[key];
+              });
+              // Place it in main variable for later use
+              self.handler.primary_keys_data[_pk] = current_pk
+            }
+        });
+
+        grid.onKeyDown.subscribe(function(event, args) {
+          var KEY_A = 65;
+          var modifiedKey = event.keyCode;
+          var isModifierDown = event.ctrlKey || event.metaKey;
+          // Intercept Ctrl/Cmd + A key board event.
+          // As we might want to load all rows before selecting all.
+          if (isModifierDown && modifiedKey == KEY_A && self.handler.has_more_rows) {
+            self.fetch_next_all(function() {
+              handleQueryOutputKeyboardEvent(event, args);
+            });
+          } else {
+            handleQueryOutputKeyboardEvent(event, args);
+          }
+        });
+
+        // Listener function which will be called when user updates existing rows
+        grid.onCellChange.subscribe(function (e, args) {
+          // self.handler.data_store.updated will holds all the updated data
+          var changed_column = args.grid.getColumns()[args.cell].field,
+            updated_data = args.item[changed_column],                   // New value for current field
+            _pk = args.item[self.client_primary_key] || null,                          // Unique key to identify row
+            column_data = {},
+            _type;
+
+          // Access to row/cell value after a cell is changed.
+          // The purpose is to remove row_id from temp_new_row
+          // if new row has primary key instead of [default_value]
+          // so that cell edit is enabled for that row.
+          var grid = args.grid,
+            row_data = grid.getDataItem(args.row),
+            is_primary_key = _.all(
+                _.values(
+                  _.pick(
+                      row_data, self.primary_keys
+                  )
+                ),
+                function(val) {
+                  return val != undefined
+                }
+              );
+
+          // temp_new_rows is available only for view data.
+          if (is_primary_key && self.handler.temp_new_rows) {
+            var index = self.handler.temp_new_rows.indexOf(args.row);
+            if (index > -1) {
+              self.handler.temp_new_rows.splice(index, 1);
+            }
+          }
+
+          column_data[changed_column] = updated_data;
+
+          if(_pk) {
+            // Check if it is in newly added row by user?
+            if(_pk in self.handler.data_store.added) {
+              _.extend(
+                self.handler.data_store.added[_pk]['data'],
+                  column_data);
+              //Find type for current column
+              self.handler.data_store.added[_pk]['err'] = false
+            // Check if it is updated data from existing rows?
+            } else if(_pk in self.handler.data_store.updated) {
+              _.extend(
+                self.handler.data_store.updated[_pk]['data'],
+                column_data
+              );
+              self.handler.data_store.updated[_pk]['err'] = false
+            } else {
+              // First updated data for this primary key
+              self.handler.data_store.updated[_pk] = {
+                'err': false, 'data': column_data,
+                'primary_keys': self.handler.primary_keys_data[_pk]
+              };
+              self.handler.data_store.updated_index[args.row] = _pk;
+            }
+          }
+          // Enable save button
+          $("#btn-save").prop('disabled', false);
+        }.bind(editor_data));
+
+        // Listener function which will be called when user adds new rows
+        grid.onAddNewRow.subscribe(function (e, args) {
+          // self.handler.data_store.added will holds all the newly added rows/data
+          var column = args.column,
+            item = args.item, data_length = this.grid.getDataLength(),
+            _key = (self.client_primary_key_counter++).toString(),
+            dataView = this.grid.getData();
+
+          // Add new row in list to keep track of it
+          if (_.isUndefined(item[0])) {
+            self.handler.temp_new_rows.push(data_length);
+          }
+
+          // If copied item has already primary key, use it.
+          if(item) {
+            item[self.client_primary_key] = _key;
+          }
+
+          dataView.addItem(item);
+          self.handler.data_store.added[_key] = {'err': false, 'data': item};
+          self.handler.data_store.added_index[data_length] = _key;
+          // Fetch data type & add it for the column
+          var temp = {};
+          temp[column.name] = _.where(this.columns, {pos: column.pos})[0]['type'];
+          grid.updateRowCount();
+          grid.render();
+
+          // Enable save button
+          $("#btn-save").prop('disabled', false);
+        }.bind(editor_data));
+
+        // Listen grid viewportChanged event to load next chunk of data.
+        grid.onViewportChanged.subscribe(function(e, args) {
+          var rendered_range = args.grid.getRenderedRange(),
+              data_len = args.grid.getDataLength();
+          // start fetching next batch of records before reaching to bottom.
+          if (self.handler.has_more_rows && !self.handler.fetching_rows && rendered_range.bottom > data_len - 100) {
+            // fetch asynchronous
+            setTimeout(self.fetch_next.bind(self));
+          }
+        })
+        // Resize SlickGrid when window resize
+        $( window ).resize( function() {
+          // Resize grid only when 'Data Output' panel is visible.
+          if(self.data_output_panel.isVisible()) {
+            self.grid_resize(grid);
+          }
+        });
+
+        // Resize SlickGrid when output Panel resize
+        self.data_output_panel.on(wcDocker.EVENT.RESIZE_ENDED, function() {
+          // Resize grid only when 'Data Output' panel is visible.
+          if(self.data_output_panel.isVisible()) {
+            self.grid_resize(grid);
+          }
+        });
+
+        // Resize SlickGrid when output Panel gets focus
+        self.data_output_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function() {
+          // Resize grid only if output panel is visible
+          if(self.data_output_panel.isVisible())
+            self.grid_resize(grid);
+        });
+
+        for (var i = 0; i < collection.length; i++) {
+          // Convert to dict from 2darray
+          var item = {};
+          for (var j = 1; j < grid_columns.length; j++) {
+            item[grid_columns[j]['field']] = collection[i][grid_columns[j]['pos']]
+          }
+
+          item[self.client_primary_key] = (self.client_primary_key_counter++).toString();
+          collection[i] = item;
+        }
+        dataView.setItems(collection, self.client_primary_key);
+      },
+      fetch_next_all: function(cb) {
+        this.fetch_next(true, cb);
+      },
+      fetch_next: function(fetch_all, cb) {
+        var self = this, url = '';
+
+        // This will prevent fetch operation if previous fetch operation is
+        // already in progress.
+        self.handler.fetching_rows = true;
+
+        $("#btn-flash").prop('disabled', true);
+
+        if (fetch_all) {
+          self.handler.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext('Fetching all records...')
+          );
+          url = url_for('sqleditor.fetch_all', {'trans_id': self.transId, 'fetch_all': 1});
+        } else {
+          url = url = url_for('sqleditor.fetch', {'trans_id': self.transId});
+        }
+
+        $.ajax({
+          url: url,
+          method: 'GET',
+          success: function(res) {
+            self.handler.has_more_rows = res.data.has_more_rows;
+            $("#btn-flash").prop('disabled', false);
+            self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
+            self.update_grid_data(res.data.result);
+            self.handler.fetching_rows = false;
+            if (typeof cb == "function") {
+              cb();
+            }
+          },
+          error: function(e) {
+            $("#btn-flash").prop('disabled', false);
+            self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
+            self.handler.has_more_rows = false;
+            self.handler.fetching_rows = false;
+            if (typeof cb == "function") {
+              cb();
+            }
+            if (e.readyState == 0) {
+              self.update_msg_history(false,
+                gettext('Not connected to the server or the connection to the server has been closed.')
+              );
+              return;
+            }
+          }
+        });
+      },
+
+      update_grid_data: function(data) {
+        this.dataView.beginUpdate();
+
+        for (var i = 0; i < data.length; i++) {
+          // Convert 2darray to dict.
+          var item = {};
+          for (var j = 1; j < this.grid_columns.length; j++) {
+            item[this.grid_columns[j]['field']] = data[i][this.grid_columns[j]['pos']]
+          }
+
+          item[this.client_primary_key] = (this.client_primary_key_counter++).toString();
+          this.dataView.addItem(item);
+        }
+
+        this.dataView.endUpdate();
+      },
+
+      /* This function is responsible to render output grid */
+      grid_resize: function(grid) {
+        var h = $($('#editor-panel').find('.wcFrame')[1]).height() - 35;
+        $('#datagrid').css({'height': h + 'px'});
+        grid.resizeCanvas();
+      },
+
+      /* This function is responsible to create and render the
+       * new backgrid for the history tab.
+       */
+      render_history_grid: function() {
+        var self = this;
+
+        self.history_collection = new HistoryBundle.HistoryCollection([]);
+
+        var historyComponent;
+        var historyCollectionReactElement = React.createElement(
+          queryHistory.QueryHistory, {
+            historyCollection: self.history_collection,
+            ref: function(component) {
+              historyComponent = component;
+            },
+          });
+        ReactDOM.render(historyCollectionReactElement, $('#history_grid')[0]);
+
+        self.history_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function () {
+          historyComponent.refocus();
+        });
+      },
+
+      // Callback function for Add New Row button click.
+      on_delete: function() {
+        var self = this;
+
+        // Trigger the addrow signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:deleterow',
+            self,
+            self.handler
+        );
+      },
+
+      _stopEventPropogation: function(ev) {
+        ev = ev || window.event;
+        ev.cancelBubble = true;
+        ev.stopPropagation();
+        ev.stopImmediatePropagation();
+        ev.preventDefault();
+      },
+
+      _closeDropDown: function(ev) {
+          var target = ev && (ev.currentTarget || ev.target);
+          if (target) {
+            $(target).closest('.open').removeClass('open').find('.dropdown-backdrop').remove();
+          }
+      },
+
+      // Callback function for Save button click.
+      on_save: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.handler.close_on_save = false;
+        // Trigger the save signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:save',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for Save button click.
+      on_save_as: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.handler.close_on_save = false;
+        // Trigger the save signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:save',
+            self,
+            self.handler,
+            true
+        );
+      },
+
+      // Callback function for the find button click.
+      on_find: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("find");
+      },
+
+      // Callback function for the find next button click.
+      on_find_next: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("findNext");
+      },
+
+      // Callback function for the find previous button click.
+      on_find_previous: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("findPrev");
+      },
+
+      // Callback function for the replace button click.
+      on_replace: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("replace");
+      },
+
+      // Callback function for the replace all button click.
+      on_replace_all: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("replaceAll");
+      },
+
+      // Callback function for the find persistent button click.
+      on_find_persistent: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("findPersistent");
+      },
+
+      // Callback function for the jump button click.
+      on_jump: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        self.query_tool_obj.execCommand("jumpToLine");
+      },
+
+      // Callback function for filter button click.
+      on_show_filter: function() {
+        var self = this;
+
+        // Trigger the show_filter signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:show_filter',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for include filter button click.
+      on_include_filter: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        // Trigger the include_filter signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:include_filter',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for exclude filter button click.
+      on_exclude_filter: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        // Trigger the exclude_filter signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:exclude_filter',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for remove filter button click.
+      on_remove_filter: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        // Trigger the remove_filter signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:remove_filter',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for ok button click.
+      on_apply: function() {
+        var self = this;
+
+        // Trigger the apply_filter signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:apply_filter',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for cancel button click.
+      on_cancel: function() {
+        $('#filter').addClass('hidden');
+        $('#editor-panel').removeClass('sql-editor-busy-fetching');
+      },
+
+      // Callback function for copy button click.
+      on_copy_row: function() {
+        var self = this;
+
+        // Trigger the copy signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:copy_row',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for paste button click.
+      on_paste_row: function() {
+        var self = this;
+
+        // Trigger the paste signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:paste_row',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for the change event of combo box
+      on_limit_change: function() {
+        var self = this;
+
+        // Trigger the limit signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:limit',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for the flash button click.
+      on_flash: function() {
+        this.handler.executeQuery();
+      },
+
+      // Callback function for the cancel query button click.
+      on_cancel_query: function() {
+        var self = this;
+
+        // Trigger the cancel-query signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:cancel-query',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for the line comment code
+      on_comment_line_code: function() {
+        this.handler.commentLineCode();
+      },
+
+      // Callback function for the line uncomment code
+      on_uncomment_line_code: function() {
+        this.handler.uncommentLineCode();
+      },
+
+      // Callback function for the block comment/uncomment code
+      on_toggle_comment_block_code: function() {
+        this.handler.commentBlockCode();
+      },
+
+      on_indent_code: function() {
+        var self = this;
+        // Trigger the comment signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:indent_selected_code',
+            self,
+            self.handler
+        );
+      },
+
+      on_unindent_code: function() {
+        var self = this;
+        // Trigger the comment signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:unindent_selected_code',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for the clear button click.
+      on_clear: function(ev) {
+        var self = this, sql;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        /* If is_query_changed flag is set to false then no need to
+         * confirm with the user for unsaved changes.
+         */
+        if (self.handler.is_query_changed) {
+          alertify.confirm(
+            gettext("Unsaved changes"),
+            gettext("Are you sure you wish to discard the current changes?"),
+            function() {
+              // Do nothing as user do not want to save, just continue
+              self.query_tool_obj.setValue('');
+            },
+            function() {
+              return true;
+            }
+          ).set('labels', {ok:'Yes', cancel:'No'});
+        } else {
+          self.query_tool_obj.setValue('');
+        }
+      },
+
+      // Callback function for the clear history button click.
+      on_clear_history: function(ev) {
+        var self = this;
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+        // ask for confirmation only if anything to clear
+        if(!self.history_collection.length()) { return; }
+
+        alertify.confirm(gettext("Clear history"),
+          gettext("Are you sure you wish to clear the history?"),
+          function() {
+            if (self.history_collection) {
+              self.history_collection.reset();
+            }
+          },
+          function() {
+            return true;
+          }
+        ).set('labels', {ok:'Yes', cancel:'No'});
+      },
+
+      // Callback function for the auto commit button click.
+      on_auto_commit: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the auto-commit signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:auto_commit',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for the auto rollback button click.
+      on_auto_rollback: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the download signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:auto_rollback',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for explain button click.
+      on_explain: function(event) {
+        this._stopEventPropogation(event);
+        this._closeDropDown(event);
+
+        this.handler.explain(event);
+      },
+
+      // Callback function for explain analyze button click.
+      on_explain_analyze: function(event) {
+        this._stopEventPropogation(event);
+        this._closeDropDown(event);
+
+        this.handler.explainAnalyze(event);
+      },
+
+      // Callback function for explain option "verbose" button click
+      on_explain_verbose: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the explain "verbose" signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:explain-verbose',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for explain option "costs" button click
+      on_explain_costs: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the explain "costs" signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:explain-costs',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for explain option "buffers" button click
+      on_explain_buffers: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the explain "buffers" signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:explain-buffers',
+            self,
+            self.handler
+        );
+      },
+
+      // Callback function for explain option "timing" button click
+      on_explain_timing: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+
+        // Trigger the explain "timing" signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:explain-timing',
+            self,
+            self.handler
+        );
+      },
+
+      do_not_close_menu: function(ev) {
+        ev.stopPropagation();
+      },
+
+      // callback function for load file button click.
+      on_file_load: function(ev) {
+        var self = this;
+
+        this._stopEventPropogation(ev);
+        this._closeDropDown(ev);
+
+        // Trigger the save signal to the SqlEditorController class
+        self.handler.trigger(
+            'pgadmin-sqleditor:button:load_file',
+            self,
+            self.handler
+        );
+      },
+
+      on_download: function() {
+        this.handler.download();
+      },
+
+      keyAction: function(event) {
+        keyboardShortcuts.processEvent(this.handler, event);
+      },
+    });
+
+    /* Defining controller class for data grid, which actually
+     * perform the operations like executing the sql query, poll the result,
+     * render the data in the grid, Save/Refresh the data etc...
+     */
+    var SqlEditorController = function(container, options) {
+      this.initialize.apply(this, arguments);
+    };
+
+    _.extend(
+      SqlEditorController.prototype,
+      Backbone.Events,
+      {
+        initialize: function(container, opts) {
+          this.container = container;
+        },
+
+        /* This function is used to create instance of SQLEditorView,
+         * call the render method of the grid view to render the backgrid
+         * header and loading icon and start execution of the sql query.
+         */
+        start: function(is_query_tool, editor_title, script_sql, is_new_browser_tab) {
+          var self = this;
+
+          self.is_query_tool = is_query_tool;
+          self.rows_affected = 0;
+          self.marked_line_no = 0;
+          self.explain_verbose = false;
+          self.explain_costs = false;
+          self.explain_buffers = false;
+          self.explain_timing = false;
+          self.is_new_browser_tab = is_new_browser_tab;
+          self.has_more_rows = false;
+          self.fetching_rows = false;
+          self.close_on_save = false;
+
+          // We do not allow to call the start multiple times.
+          if (self.gridView)
+            return;
+
+          self.gridView = new SQLEditorView({
+            el: self.container,
+            handler: self
+          });
+          self.transId = self.gridView.transId = self.container.data('transId');
+
+          self.gridView.editor_title = _.unescape(editor_title);
+          self.gridView.current_file = undefined;
+
+          // Render the header
+          self.gridView.render();
+
+          // Listen to the file manager button events
+          pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:select_file', self._select_file_handler, self);
+          pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', self._save_file_handler, self);
+
+          // Listen to the codemirror on text change event
+          // only in query editor tool
+          if (self.is_query_tool) {
+            self.get_preferences();
+            self.gridView.query_tool_obj.on('change', self._on_query_change.bind(self));
+          }
+
+          // Listen on events come from SQLEditorView for the button clicked.
+          self.on('pgadmin-sqleditor:button:load_file', self._load_file, self);
+          self.on('pgadmin-sqleditor:button:save', self._save, self);
+          self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
+          self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+          self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
+          self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
+          self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
+          self.on('pgadmin-sqleditor:button:apply_filter', self._apply_filter, self);
+          self.on('pgadmin-sqleditor:button:copy_row', self._copy_row, self);
+          self.on('pgadmin-sqleditor:button:paste_row', self._paste_row, self);
+          self.on('pgadmin-sqleditor:button:limit', self._set_limit, self);
+          self.on('pgadmin-sqleditor:button:cancel-query', self._cancel_query, self);
+          self.on('pgadmin-sqleditor:button:auto_rollback', self._auto_rollback, self);
+          self.on('pgadmin-sqleditor:button:auto_commit', self._auto_commit, self);
+          self.on('pgadmin-sqleditor:button:explain-verbose', self._explain_verbose, self);
+          self.on('pgadmin-sqleditor:button:explain-costs', self._explain_costs, self);
+          self.on('pgadmin-sqleditor:button:explain-buffers', self._explain_buffers, self);
+          self.on('pgadmin-sqleditor:button:explain-timing', self._explain_timing, self);
+          // Indentation related
+          self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self);
+          self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self);
+
+          if (self.is_query_tool) {
+            self.gridView.query_tool_obj.refresh();
+            if(script_sql && script_sql !== '') {
+              self.gridView.query_tool_obj.setValue(script_sql);
+            }
+          }
+          else {
+            // Disable codemirror by setting cursor to nocursor and background to dark.
+            self.gridView.query_tool_obj.setOption("readOnly", 'nocursor');
+            var cm = self.gridView.query_tool_obj.getWrapperElement();
+            if (cm) {
+              cm.className += ' bg-gray-1 opacity-5';
+            }
+            self.disable_tool_buttons(true);
+            self._execute_data_query();
+          }
+        },
+
+        // This function checks if there is any dirty data in the grid before
+        // it executes the sql query
+        _execute_data_query: function() {
+          var self = this;
+
+          // Check if the data grid has any changes before running query
+          if(_.has(self, 'data_store') &&
+                ( _.size(self.data_store.added) ||
+                _.size(self.data_store.updated) ||
+                _.size(self.data_store.deleted))
+            ) {
+                alertify.confirm(gettext("Unsaved changes"),
+                  gettext("The data has been modified, but not saved. Are you sure you wish to discard the changes?"),
+                  function(){
+                    // Do nothing as user do not want to save, just continue
+                    self._run_query();
+                  },
+                  function(){
+                    // Stop, User wants to save
+                    return true;
+                  }
+                ).set('labels', {ok:'Yes', cancel:'No'});
+          } else {
+            self._run_query();
+          }
+        },
+
+        // This function makes the ajax call to execute the sql query.
+        _run_query: function() {
+          var self = this;
+          self.query_start_time = new Date();
+          self.rows_affected = 0;
+          self._init_polling_flags();
+          // keep track of newly added rows
+          self.rows_to_disable = new Array();
+          // Temporarily hold new rows added
+          self.temp_new_rows = new Array();
+          self.has_more_rows = false;
+          self.fetching_rows = false;
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Initializing query execution.")
+          );
+
+          $("#btn-flash").prop('disabled', true);
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:message',
+            gettext("Waiting for the query execution to complete...")
+          );
+
+          $.ajax({
+            url: url_for('sqleditor.view_data_start', {'trans_id': self.transId}),
+            method: 'GET',
+            success: function(res) {
+              if (res.data.status) {
+
+                self.can_edit = res.data.can_edit;
+                self.can_filter = res.data.can_filter;
+                self.info_notifier_timeout = res.data.info_notifier_timeout;
+
+                // Set the sql query to the SQL panel
+                self.gridView.query_tool_obj.setValue(res.data.sql);
+                self.query = res.data.sql;
+
+
+                /* If filter is applied then remove class 'btn-default'
+                 * and add 'btn-warning' to change the colour of the button.
+                 */
+                if (self.can_filter && res.data.filter_applied) {
+                  $('#btn-filter').removeClass('btn-default');
+                  $('#btn-filter-dropdown').removeClass('btn-default');
+                  $('#btn-filter').addClass('btn-warning');
+                  $('#btn-filter-dropdown').addClass('btn-warning');
+                }
+                else {
+                  $('#btn-filter').removeClass('btn-warning');
+                  $('#btn-filter-dropdown').removeClass('btn-warning');
+                  $('#btn-filter').addClass('btn-default');
+                  $('#btn-filter-dropdown').addClass('btn-default');
+                }
+                $("#btn-save").prop('disabled', true);
+                $("#btn-file-menu-dropdown").prop('disabled', true);
+                $("#btn-copy-row").prop('disabled', true);
+                $("#btn-paste-row").prop('disabled', true);
+
+                // Set the combo box value
+                $(".limit").val(res.data.limit);
+
+                // If status is True then poll the result.
+                self._poll();
+              }
+              else {
+                self.trigger('pgadmin-sqleditor:loading-icon:hide');
+                self.update_msg_history(false, res.data.result);
+              }
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              if (e.readyState == 0) {
+                self.update_msg_history(false,
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+                );
+                return;
+              }
+
+              var msg = e.responseText;
+              if (e.responseJSON != undefined &&
+                e.responseJSON.errormsg != undefined)
+                msg = e.responseJSON.errormsg;
+
+              self.update_msg_history(false, msg);
+            }
+          });
+        },
+
+        // This is a wrapper to call_render function
+        // We need this because we have separated columns route & result route
+        // We need to combine both result here in wrapper before rendering grid
+        call_render_after_poll: function(res) {
+          var self = this;
+          self.query_end_time = new Date();
+          self.rows_affected = res.rows_affected,
+          self.has_more_rows = res.has_more_rows;
+
+          /* If no column information is available it means query
+             runs successfully with no result to display. In this
+             case no need to call render function.
+          */
+          if (res.colinfo != null)
+            self._render(res);
+          else {
+            // Show message in message and history tab in case of query tool
+            self.total_time = self.get_query_run_time(self.query_start_time, self.query_end_time);
+            var msg = S(gettext("Query returned successfully in %s.")).sprintf(self.total_time).value();
+            res.result += "\n\n" + msg;
+            self.update_msg_history(true, res.result, false);
+            // Display the notifier if the timeout is set to >= 0
+            if (self.info_notifier_timeout >= 0) {
+              var alertifyWrapper = new AlertifyWrapper();
+              alertifyWrapper.success(msg, self.info_notifier_timeout);
+            }
+          }
+
+          // Enable/Disable query tool button only if is_query_tool is true.
+          if (self.is_query_tool) {
+            self.disable_tool_buttons(false);
+            $("#btn-cancel-query").prop('disabled', true);
+          }
+          is_query_running = false;
+          self.trigger('pgadmin-sqleditor:loading-icon:hide');
+        },
+
+
+        /* This function makes the ajax call to poll the result,
+         * if status is Busy then recursively call the poll function
+         * till the status is 'Success' or 'NotConnected'. If status is
+         * 'Success' then call the render method to render the data.
+         */
+        _poll: function() {
+          var self = this;
+
+          setTimeout(
+            function() {
+              $.ajax({
+                url: url_for('sqleditor.poll', {'trans_id': self.transId}),
+                method: 'GET',
+                success: function(res) {
+                  if (res.data.status === 'Success') {
+                    self.trigger(
+                      'pgadmin-sqleditor:loading-icon:message',
+                      gettext("Loading data from the database server and rendering...")
+                    );
+
+                    self.call_render_after_poll(res.data);
+                  }
+                  else if (res.data.status === 'Busy') {
+                    // If status is Busy then poll the result by recursive call to the poll function
+                    self._poll();
+                    is_query_running = true;
+                    if (res.data.result) {
+                      self.update_msg_history(res.data.status, res.data.result, false);
+                    }
+                  }
+                  else if (res.data.status === 'NotConnected') {
+                    self.trigger('pgadmin-sqleditor:loading-icon:hide');
+                    // Enable/Disable query tool button only if is_query_tool is true.
+                    if (self.is_query_tool) {
+                      self.disable_tool_buttons(false);
+                      $("#btn-cancel-query").prop('disabled', true);
+                    }
+                    self.update_msg_history(false, res.data.result, true);
+                  }
+                  else if (res.data.status === 'Cancel') {
+                    self.trigger('pgadmin-sqleditor:loading-icon:hide');
+                    self.update_msg_history(false, "Execution Cancelled!", true)
+                  }
+                },
+                error: function(e) {
+                  // Enable/Disable query tool button only if is_query_tool is true.
+                  self.resetQueryHistoryObject(self);
+                  self.trigger('pgadmin-sqleditor:loading-icon:hide');
+                  if (self.is_query_tool) {
+                    self.disable_tool_buttons(false);
+                    $("#btn-cancel-query").prop('disabled', true);
+                  }
+
+                  if (e.readyState == 0) {
+                    self.update_msg_history(false,
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  self.update_msg_history(false, msg);
+                  // Highlight the error in the sql panel
+                  self._highlight_error(msg);
+                }
+              });
+          }, self.POLL_FALLBACK_TIME());
+        },
+
+        /* This function is used to create the backgrid columns,
+         * create the Backbone PageableCollection and finally render
+         * the data in the backgrid.
+         */
+        _render: function(data) {
+          var self = this;
+          self.colinfo = data.col_info;
+          self.primary_keys = data.primary_keys;
+          self.client_primary_key = data.client_primary_key;
+          self.cell_selected = false;
+          self.selected_model = null;
+          self.changedModels = [];
+          $('.sql-editor-explain').empty();
+
+          /* If object don't have primary keys then set the
+           * can_edit flag to false.
+           */
+          if (self.primary_keys === null || self.primary_keys === undefined
+              || _.size(self.primary_keys) === 0)
+            self.can_edit = false;
+          else
+            self.can_edit = true;
+
+          /* If user can filter the data then we should enabled
+           * Filter and Limit buttons.
+           */
+          if (self.can_filter) {
+            $(".limit").prop('disabled', false);
+            $(".limit").addClass('limit-enabled');
+            $("#btn-filter").prop('disabled', false);
+            $("#btn-filter-dropdown").prop('disabled', false);
+          }
+
+          // Initial settings for delete row, copy row and paste row buttons.
+          $("#btn-delete-row").prop('disabled', true);
+          // Do not disable save button in query tool
+          if(!self.is_query_tool && !self.can_edit) {
+            $("#btn-save").prop('disabled', true);
+            $("#btn-file-menu-dropdown").prop('disabled', true);
+          }
+          if (!self.can_edit) {
+            $("#btn-delete-row").prop('disabled', true);
+            $("#btn-copy-row").prop('disabled', true);
+            $("#btn-paste-row").prop('disabled', true);
+          }
+
+          // Fetch the columns metadata
+          self._fetch_column_metadata.call(
+            self, data, function() {
+              var self = this;
+
+              self.trigger(
+                'pgadmin-sqleditor:loading-icon:message',
+                gettext("Loading data from the database server and rendering..."),
+                self
+              );
+
+              // Show message in message and history tab in case of query tool
+              self.total_time = self.get_query_run_time(self.query_start_time, self.query_end_time);
+              var msg1 = S(gettext("Successfully run. Total query runtime: %s.")).sprintf(self.total_time).value();
+              var msg2 = S(gettext("%s rows affected.")).sprintf(self.rows_affected).value();
+
+              // Display the notifier if the timeout is set to >= 0
+              if (self.info_notifier_timeout >= 0) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(msg1 + ' ' + msg2, self.info_notifier_timeout);
+              }
+
+              var _msg = msg1 + '\n' + msg2;
+
+              self.update_msg_history(true, _msg, false);
+              // If there is additional messages from server then add it to message
+              if(!_.isNull(data.additional_messages) &&
+                    !_.isUndefined(data.additional_messages)) {
+                    _msg = data.additional_messages + '\n' + _msg;
+              }
+
+              $('.sql-editor-message').text(_msg);
+
+              /* Add the data to the collection and render the grid.
+               * In case of Explain draw the graph on explain panel
+               * and add json formatted data to collection and render.
+               */
+              var explain_data_array = [];
+              if (
+                data.result && data.result.length >= 1 &&
+                data.result[0] && data.result[0][0] && data.result[0][0][0] &&
+                data.result[0][0][0].hasOwnProperty('Plan') &&
+                _.isObject(data.result[0][0][0]['Plan'])
+              ) {
+                var explain_data = [JSON.stringify(data.result[0][0], null, 2)];
+                explain_data_array.push(explain_data);
+                // Make sure - the 'Data Output' panel is visible, before - we
+                // start rendering the grid.
+                self.gridView.data_output_panel.focus();
+                setTimeout(
+                  function() {
+                    self.gridView.render_grid(
+                      explain_data_array, self.columns, self.can_edit,
+                      self.client_primary_key
+                    );
+                    // Make sure - the 'Explain' panel is visible, before - we
+                    // start rendering the grid.
+                    self.gridView.explain_panel.focus();
+                    pgExplain.DrawJSONPlan(
+                      $('.sql-editor-explain'), data.result[0][0]
+                    );
+                  }, 10
+                );
+              } else {
+                // Make sure - the 'Data Output' panel is visible, before - we
+                // start rendering the grid.
+                self.gridView.data_output_panel.focus();
+                setTimeout(
+                  function() {
+                    self.gridView.render_grid(data.result, self.columns,
+                    self.can_edit, self.client_primary_key, data.rows_affected);
+                  }, 10
+                );
+              }
+
+              // Hide the loading icon
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              $("#btn-flash").prop('disabled', false);
+            }.bind(self)
+          );
+        },
+
+        // This function creates the columns as required by the backgrid
+        _fetch_column_metadata: function(data, cb) {
+          var colinfo = data.colinfo,
+              primary_keys = data.primary_keys,
+              result = data.result,
+              columns = [],
+              self = this;
+          // Store pg_types in an array
+          var pg_types = new Array();
+          _.each(data.types, function(r) {
+            pg_types[r.oid] = [r.typname];
+          });
+
+          // Create columns required by slick grid to render
+          _.each(colinfo, function(c) {
+            var is_primary_key = false;
+
+            // Check whether table have primary key
+            if (_.size(primary_keys) > 0) {
+              _.each(primary_keys, function (value, key) {
+                if (key === c.name)
+                  is_primary_key = true;
+              });
+            }
+
+            // To show column label and data type in multiline,
+            // The elements should be put inside the div.
+            // Create column label and type.
+            var col_type = '',
+              column_label = '',
+              col_cell;
+            var type = pg_types[c.type_code] ?
+                         pg_types[c.type_code][0] :
+                         // This is the case where user might
+                         // have use casting so we will use type
+                         // returned by cast function
+                         pg_types[pg_types.length - 1][0] ?
+                           pg_types[pg_types.length - 1][0] : 'unknown';
+
+            if (!is_primary_key)
+              col_type += type;
+            else
+              col_type += '[PK] ' + type;
+
+            if (c.precision && c.precision >= 0 && c.precision != 65535) {
+              col_type += ' (' + c.precision;
+              col_type += c.scale && c.scale != 65535 ?
+                          ',' + c.scale + ')':
+                          ')';
+            }
+
+            // Identify cell type of column.
+            switch(type) {
+              case "json":
+              case "json[]":
+              case "jsonb":
+              case "jsonb[]":
+                col_cell = 'Json';
+                break;
+              case "smallint":
+              case "integer":
+              case "bigint":
+              case "decimal":
+              case "numeric":
+              case "real":
+              case "double precision":
+                col_cell = 'number';
+                break;
+              case "boolean":
+                col_cell = 'boolean';
+                break;
+              case "character":
+              case "character[]":
+              case "character varying":
+              case "character varying[]":
+                if (c.internal_size && c.internal_size >= 0 && c.internal_size != 65535) {
+                  // Update column type to display length on column header
+                  col_type += ' (' + c.internal_size + ')';
+                }
+                col_cell = 'string';
+                break;
+              default:
+                col_cell = 'string';
+            }
+
+            column_label = c.display_name + '<br>' + col_type;
+
+            var col = {
+              'name': c.name,
+              'display_name': c.display_name,
+              'column_type': col_type,
+              'column_type_internal': type,
+              'pos': c.pos,
+              'label': column_label,
+              'cell': col_cell,
+              'can_edit': self.can_edit,
+              'type': type,
+              'not_null': c.not_null,
+              'has_default_val': c.has_default_val
+            };
+            columns.push(col);
+          });
+
+          self.columns = columns;
+          if (cb && typeof(cb) == 'function') {
+            cb();
+          }
+        },
+
+        resetQueryHistoryObject: function (history) {
+          history.total_time = '-';
+        },
+
+        // This function is used to raise appropriate message.
+        update_msg_history: function(status, msg, clear_grid) {
+          var self = this;
+          if (clear_grid === undefined)
+            clear_grid = true;
+
+          self.gridView.messages_panel.focus();
+
+          if (clear_grid) {
+            // Delete grid
+            if (self.gridView.handler.slickgrid) {
+              self.gridView.handler.slickgrid.destroy();
+
+            }
+            // Misc cleaning
+            self.columns = undefined;
+            self.collection = undefined;
+
+            $('.sql-editor-message').text(msg);
+          } else {
+            $('.sql-editor-message').append(msg);
+          }
+
+          // Scroll automatically when msgs appends to element
+          setTimeout(function(){
+            $(".sql-editor-message").scrollTop($(".sql-editor-message")[0].scrollHeight);;
+          }, 10);
+
+          if(status != 'Busy') {
+            $("#btn-flash").prop('disabled', false);
+            self.trigger('pgadmin-sqleditor:loading-icon:hide');
+            self.gridView.history_collection.add({
+              'status' : status,
+              'start_time': self.query_start_time,
+              'query': self.query,
+              'row_affected': self.rows_affected,
+              'total_time': self.total_time,
+              'message':msg,
+            });
+          }
+        },
+
+        // This function will return the total query execution Time.
+        get_query_run_time: function (start_time, end_time) {
+          var self = this;
+
+          // Calculate the difference in milliseconds
+          var difference_ms, miliseconds;
+          difference_ms = miliseconds = end_time.getTime() - start_time.getTime();
+          //take out milliseconds
+          difference_ms = difference_ms/1000;
+          var seconds = Math.floor(difference_ms % 60);
+          difference_ms = difference_ms/60;
+          var minutes = Math.floor(difference_ms % 60);
+
+          if (minutes > 0)
+            return minutes + ' min';
+          else if (seconds > 0) {
+            return seconds + ' secs';
+          }
+          else
+            return miliseconds + ' msec';
+        },
+
+        /* This function is used to check whether cell
+         * is editable or not depending on primary keys
+         * and staged_rows flag
+         */
+        is_editable: function(obj) {
+          var self = this;
+          if (obj instanceof Backbone.Collection)
+            return false;
+          return (self.get('can_edit'));
+        },
+
+        rows_to_delete: function(data) {
+          var self = this,
+            tmp_keys = self.primary_keys;
+
+          // re-calculate rows with no primary keys
+          self.temp_new_rows = [];
+          data.forEach(function(d, idx) {
+            var p_keys_list = _.pick(d, tmp_keys),
+              is_primary_key = Object.keys(p_keys_list).length ?
+                               p_keys_list[0] : undefined;
+
+            if (!is_primary_key) {
+              self.temp_new_rows.push(idx);
+            }
+          });
+          self.rows_to_disable = _.clone(self.temp_new_rows);
+        },
+
+        // This function will delete selected row.
+        _delete: function() {
+          var self = this, deleted_keys = [],
+              dgrid = document.getElementById("datagrid"),
+              is_added = _.size(self.data_store.added),
+              is_updated = _.size(self.data_store.updated);
+
+          // Remove newly added rows from staged rows as we don't want to send them on server
+          if(is_added) {
+            _.each(self.data_store.added, function(val, key) {
+              if(key in self.data_store.staged_rows) {
+                // Remove the row from data store so that we do not send it on server
+                deleted_keys.push(key);
+                delete self.data_store.staged_rows[key];
+                delete self.data_store.added[key];
+                delete self.data_store.added_index[key];
+              }
+            });
+          }
+          // If only newly rows to delete and no data is there to send on server
+          // then just re-render the grid
+          if(_.size(self.data_store.staged_rows) == 0) {
+              var grid = self.slickgrid,
+              dataView = grid.getData(),
+              data = dataView.getItems(),
+              idx = 0;
+
+              grid.resetActiveCell();
+
+              dataView.beginUpdate();
+              for (var i = 0; i < deleted_keys.length; i++) {
+                dataView.deleteItem(deleted_keys[i]);
+              }
+              dataView.endUpdate();
+              self.rows_to_delete.apply(self, [dataView.getItems()]);
+              grid.resetActiveCell();
+              grid.setSelectedRows([]);
+              grid.invalidate();
+
+              // Nothing to copy or delete here
+              $("#btn-delete-row").prop('disabled', true);
+              $("#btn-copy-row").prop('disabled', true);
+              if(_.size(self.data_store.added) || is_updated) {
+                // Do not disable save button if there are
+                // any other changes present in grid data
+                $("#btn-save").prop('disabled', false);
+              } else {
+                $("#btn-save").prop('disabled', true);
+              }
+              var alertifyWrapper = new AlertifyWrapper();
+              alertifyWrapper.success(gettext("Row(s) deleted"));
+          } else {
+            // There are other data to needs to be updated on server
+            if(is_updated) {
+              alertify.alert(gettext("Operation failed"),
+                    gettext("There are unsaved changes in grid, Please save them first to avoid inconsistency in data")
+                  );
+              return;
+            }
+            alertify.confirm(gettext("Delete Row(s)"),
+                  gettext("Are you sure you wish to delete selected row(s)?"),
+              function() {
+                $("#btn-delete-row").prop('disabled', true);
+                $("#btn-copy-row").prop('disabled', true);
+                // Change the state
+                self.data_store.deleted = self.data_store.staged_rows;
+                self.data_store.staged_rows = {};
+                // Save the changes on server
+                self._save();
+              },
+              function() {
+                // Do nothing as user canceled the operation.
+              }
+            ).set('labels', {ok: gettext("Yes"), cancel:gettext("No")});
+          }
+        },
+
+        /* This function will fetch the list of changed models and make
+         * the ajax call to save the data into the database server.
+         * and will open save file dialog conditionally.
+         */
+        _save: function(view, controller, save_as) {
+          var self = this,
+              data = [],
+              save_data = true;
+
+          // Open save file dialog if query tool
+          if (self.is_query_tool) {
+            var current_file = self.gridView.current_file;
+            if (!_.isUndefined(current_file) && !save_as) {
+              self._save_file_handler(current_file);
+            }
+            else {
+              // provide custom option to save file dialog
+              var params = {
+                'supported_types': ["*", "sql"],
+                'dialog_type': 'create_file',
+                'dialog_title': 'Save File',
+                'btn_primary': 'Save'
+              }
+              pgAdmin.FileManager.init();
+              pgAdmin.FileManager.show_dialog(params);
+            }
+            return;
+          }
+          $("#btn-save").prop('disabled', true);
+          $("#btn-file-menu-dropdown").prop('disabled', true);
+
+          var is_added = _.size(self.data_store.added),
+            is_updated = _.size(self.data_store.updated),
+            is_deleted = _.size(self.data_store.deleted),
+            is_primary_error = false;
+
+          if( !is_added && !is_updated && !is_deleted ) {
+            return;  // Nothing to save here
+          }
+
+          if (save_data) {
+
+            self.trigger(
+              'pgadmin-sqleditor:loading-icon:show',
+              gettext("Saving the updated data...")
+            );
+
+            // Add the columns to the data so the server can remap the data
+            var req_data = self.data_store;
+            req_data.columns = view ? view.handler.columns : self.columns;
+
+            // Make ajax call to save the data
+            $.ajax({
+              url: url_for('sqleditor.save', {'trans_id': self.transId}),
+              method: 'POST',
+              contentType: "application/json",
+              data: JSON.stringify(req_data),
+              success: function(res) {
+                var grid = self.slickgrid,
+                    dataView = grid.getData(),
+                    data_length = dataView.getLength(),
+                    data = [];
+                if (res.data.status) {
+                    // Remove flag is_row_copied from copied rows
+                    _.each(data, function(row, idx) {
+                      if (row.is_row_copied) {
+                        delete row.is_row_copied;
+                      }
+                    });
+
+                    // Remove 2d copied_rows array
+                    if (grid.copied_rows) {
+                      delete grid.copied_rows;
+                    }
+
+                    // Remove deleted rows from client as well
+                    if(is_deleted) {
+                      var rows = grid.getSelectedRows();
+                      if(data_length == rows.length) {
+                        // This means all the rows are selected, clear all data
+                        data = [];
+                        dataView.setItems(data, self.client_primary_key);
+                      } else {
+                        dataView.beginUpdate();
+                        for (var i = 0; i < rows.length; i++) {
+                          var item = grid.getDataItem(rows[i]);
+                          data.push(item);
+                          dataView.deleteItem(item[self.client_primary_key]);
+                        }
+                        dataView.endUpdate();
+                      }
+                      self.rows_to_delete.apply(self, [data]);
+                      grid.setSelectedRows([]);
+                    }
+
+                    // whether a cell is editable or not is decided in
+                    // grid.onBeforeEditCell function (on cell click)
+                    // but this function should do its job after save
+                    // operation. So assign list of added rows to original
+                    // rows_to_disable array.
+                    if (is_added) {
+                       self.rows_to_disable = _.clone(self.temp_new_rows);
+                    }
+
+                    grid.setSelectedRows([]);
+                    // Reset data store
+                    self.data_store = {
+                      'added': {},
+                      'updated': {},
+                      'deleted': {},
+                      'added_index': {},
+                      'updated_index': {}
+                    }
+
+                    // Reset old primary key data now
+                    self.primary_keys_data = {};
+
+                    // Clear msgs after successful save
+                    $('.sql-editor-message').html('');
+                } else {
+                  // Something went wrong while saving data on the db server
+                  $("#btn-flash").prop('disabled', false);
+                  $('.sql-editor-message').text(res.data.result);
+                  var err_msg = S(gettext("%s.")).sprintf(res.data.result).value();
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(err_msg, 20);
+                  grid.setSelectedRows([]);
+                  // To highlight the row at fault
+                  if(_.has(res.data, '_rowid') &&
+                      (!_.isUndefined(res.data._rowid)|| !_.isNull(res.data._rowid))) {
+                    var _row_index = self._find_rowindex(res.data._rowid);
+                    if(_row_index in self.data_store.added_index) {
+                      // Remove new row index from temp_list if save operation
+                      // fails
+                      var index = self.handler.temp_new_rows.indexOf(res.data._rowid);
+                      if (index > -1) {
+                         self.handler.temp_new_rows.splice(index, 1);
+                      }
+                     self.data_store.added[self.data_store.added_index[_row_index]].err = true
+                    } else if (_row_index in self.data_store.updated_index) {
+                     self.data_store.updated[self.data_store.updated_index[_row_index]].err = true
+                    }
+                  }
+                  grid.gotoCell(_row_index, 1);
+                }
+
+                // Update the sql results in history tab
+                _.each(res.data.query_result, function(r) {
+                  self.gridView.history_collection.add({
+                    'status': r.status,
+                    'start_time': self.query_start_time,
+                    'query': r.sql,
+                    'row_affected': r.rows_affected,
+                    'total_time': self.total_time,
+                    'message': r.result,
+                  });
+                });
+                self.trigger('pgadmin-sqleditor:loading-icon:hide');
+
+                grid.invalidate();
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(gettext("Data saved successfully."));
+                if (self.close_on_save) {
+                  self.close();
+                }
+              },
+              error: function(e) {
+                if (e.readyState == 0) {
+                  self.update_msg_history(false,
+                    gettext("Not connected to the server or the connection to the server has been closed.")
+                  );
+                  return;
+                }
+
+                var msg = e.responseText;
+                if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                  msg = e.responseJSON.errormsg;
+
+                self.update_msg_history(false, msg);
+              }
+            });
+          }
+        },
+
+        // Find index of row at fault from grid data
+        _find_rowindex: function(rowid) {
+          var self = this,
+            grid = self.slickgrid,
+            dataView = grid.getData(),
+            data = dataView.getItems(),
+            _rowid,
+            count = 0,
+            _idx = -1;
+
+          // If _rowid is object then it's update/delete operation
+          if(_.isObject(rowid)) {
+              _rowid = rowid;
+          } else if (_.isString(rowid)) { // Insert operation
+            var rowid = {};
+            rowid[self.client_primary_key]= rowid;
+            _rowid = rowid;
+          } else {
+            // Something is wrong with unique id
+            return _idx;
+          }
+
+          _.find(data, function(d) {
+            // search for unique id in row data if found than its the row
+            // which error out on server side
+            var tmp = [];  //_.findWhere needs array of object to work
+            tmp.push(d);
+            if(_.findWhere(tmp, _rowid)) {
+              _idx = count;
+              // Now exit the loop by returning true
+              return true;
+            }
+              count++;
+          });
+
+          // Not able to find in grid Data
+          return _idx;
+        },
+
+        // Save as
+        _save_as: function() {
+          return this._save(true);
+        },
+
+        // Set panel title.
+        setTitle: function(title) {
+          var self = this;
+
+          if (self.is_new_browser_tab) {
+            window.document.title = title;
+          } else {
+            _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
+              if(p.isVisible()) {
+                p.title(decodeURIComponent(title));
+              }
+            });
+          }
+        },
+
+        // load select file dialog
+        _load_file: function() {
+          var self = this;
+
+          /* If is_query_changed flag is set to false then no need to
+           * confirm with the user for unsaved changes.
+           */
+          if (self.is_query_changed) {
+            alertify.confirm(gettext("Unsaved changes"),
+              gettext("Are you sure you wish to discard the current changes?"),
+              function() {
+                // User do not want to save, just continue
+                self._open_select_file_manager();
+              },
+              function() {
+                return true;
+              }
+            ).set('labels', {ok:'Yes', cancel:'No'});
+          } else {
+            self._open_select_file_manager();
+          }
+
+        },
+
+        // Open FileManager
+        _open_select_file_manager: function() {
+          var params = {
+            'supported_types': ["sql"], // file types allowed
+            'dialog_type': 'select_file' // open select file dialog
+          }
+          pgAdmin.FileManager.init();
+          pgAdmin.FileManager.show_dialog(params);
+        },
+
+        // read file data and return as response
+        _select_file_handler: function(e) {
+          var self = this,
+              data = {
+                'file_name': decodeURI(e)
+              };
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Loading the file...")
+          );
+          // set cursor to progress before file load
+          var $busy_icon_div = $('.sql-editor-busy-fetching');
+          $busy_icon_div.addClass('show_progress');
+
+          // Make ajax call to load the data from file
+          $.ajax({
+            url: url_for('sqleditor.load_file'),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              self.gridView.query_tool_obj.setValue(res);
+              self.gridView.current_file = e;
+              self.setTitle(self.gridView.current_file.split('\\').pop().split('/').pop());
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              // hide cursor
+              $busy_icon_div.removeClass('show_progress');
+
+              // disable save button on file save
+              $("#btn-save").prop('disabled', true);
+              $("#btn-file-menu-save").css('display', 'none');
+
+              // Update the flag as new content is just loaded.
+              self.is_query_changed = false;
+            },
+            error: function(e) {
+              var errmsg = $.parseJSON(e.responseText).errormsg;
+              var alertifyWrapper = new AlertifyWrapper();
+              alertifyWrapper.error(errmsg);
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              // hide cursor
+              $busy_icon_div.removeClass('show_progress');
+            }
+          });
+        },
+
+        // read data from codemirror and write to file
+        _save_file_handler: function(e) {
+          var self = this,
+            data = {
+              'file_name': decodeURI(e),
+              'file_content': self.gridView.query_tool_obj.getValue()
+            };
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Saving the queries in the file...")
+          );
+
+          // Make ajax call to save the data to file
+          $.ajax({
+            url: url_for('sqleditor.save_file'),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              if (res.data.status) {
+                var alertifyWrapper = new AlertifyWrapper();
+                alertifyWrapper.success(gettext("File saved successfully."));
+                self.gridView.current_file = e;
+                self.setTitle(self.gridView.current_file.replace(/^.*[\\\/]/g, ''));
+                // disable save button on file save
+                $("#btn-save").prop('disabled', true);
+                $("#btn-file-menu-save").css('display', 'none');
+
+                // Update the flag as query is already saved.
+                self.is_query_changed = false;
+              }
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              if (self.close_on_save) {
+                self.close()
+              }
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+
+              var errmsg = $.parseJSON(e.responseText).errormsg;
+              setTimeout(
+                function() {
+                  var alertifyWrapper = new AlertifyWrapper();
+                  alertifyWrapper.error(errmsg);
+                }, 10
+              );
+            },
+          });
+        },
+
+        // codemirror text change event
+        _on_query_change: function(query_tool_obj) {
+          var self = this;
+
+          if (!self.is_query_changed) {
+            // Update the flag as query is going to changed.
+            self.is_query_changed = true;
+
+            if(self.gridView.current_file) {
+              var title = self.gridView.current_file.replace(/^.*[\\\/]/g, '') + ' *'
+              self.setTitle(title);
+            } else {
+              var title = '';
+
+              if (self.is_new_browser_tab) {
+                title = window.document.title + ' *';
+              } else {
+                // Find the title of the visible panel
+                _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
+                  if(p.isVisible()) {
+                    self.gridView.panel_title = p._title;
+                  }
+                });
+
+                title = self.gridView.panel_title + ' *';
+              }
+              self.setTitle(title);
+            }
+
+            $("#btn-save").prop('disabled', false);
+            $("#btn-file-menu-save").css('display', 'block');
+            $("#btn-file-menu-dropdown").prop('disabled', false);
+          }
+        },
+
+
+        // This function will run the SQL query and refresh the data in the backgrid.
+        executeQuery: function() {
+          var self = this;
+
+          // Start execution of the query.
+          if (self.is_query_tool) {
+            $('.sql-editor-message').html('');
+            self._execute();
+          } else {
+            self._execute_data_query();
+          }
+        },
+
+        // This function will set the required flag for polling response data
+        _init_polling_flags: function() {
+          var self = this;
+
+          // To get a timeout for polling fallback timer in seconds in
+          // regards to elapsed time
+          self.POLL_FALLBACK_TIME = function() {
+            var seconds = parseInt((Date.now() - self.query_start_time.getTime())/1000);
+            // calculate & return fall back polling timeout
+            if(seconds >= 10 && seconds < 30) {
+              return 500;
+            }
+            else if(seconds >= 30 && seconds < 60) {
+              return 1000;
+            }
+            else if(seconds >= 60 && seconds < 90) {
+              return 2000;
+            }
+            else if(seconds >= 90) {
+              return 5000;
+            }
+            else
+              return 1;
+          }
+        },
+
+        // This function will show the filter in the text area.
+        _show_filter: function() {
+          var self = this;
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Loading the existing filter options...")
+          );
+          $.ajax({
+            url: url_for('sqleditor.get_filter', {'trans_id': self.transId}),
+            method: 'GET',
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              if (res.data.status) {
+                $('#filter').removeClass('hidden');
+                $('#editor-panel').addClass('sql-editor-busy-fetching');
+                self.gridView.filter_obj.refresh();
+
+                if (res.data.result == null)
+                  self.gridView.filter_obj.setValue('');
+                else
+                  self.gridView.filter_obj.setValue(res.data.result);
+              } else {
+                setTimeout(
+                  function() {
+                    alertify.alert('Get Filter Error', res.data.result);
+                  }, 10
+                );
+              }
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+
+              var msg;
+              if (e.readyState == 0) {
+                msg =
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+              } else {
+                msg = e.responseText;
+                if (e.responseJSON != undefined &&
+                  e.responseJSON.errormsg != undefined)
+                  msg = e.responseJSON.errormsg;
+              }
+              setTimeout(
+                function() {
+                  alertify.alert('Get Filter Error', msg);
+                }, 10
+              );
+            }
+          });
+        },
+
+        // This function will include the filter by selection.
+        _include_filter: function () {
+          var self = this,
+              data = {}, grid, active_column, column_info, _values;
+
+         grid = self.slickgrid;
+         active_column = grid.getActiveCell();
+
+          // If no cell is selected then return from the function
+          if (_.isNull(active_column) || _.isUndefined(active_column))
+            return;
+
+          column_info = grid.getColumns()[active_column.cell]
+
+          // Fetch current row data from grid
+          _values = grid.getDataItem(active_column.row, active_column.cell)
+          if (_.isNull(_values) || _.isUndefined(_values))
+            return;
+
+          // Add column position and it's value to data
+          data[column_info.field] = _values[column_info.field] || '';
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Applying the new filter...")
+          );
+
+          // Make ajax call to include the filter by selection
+          $.ajax({
+            url: url_for('sqleditor.inclusive_filter', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (res.data.status) {
+                    // Refresh the sql grid
+                    self.executeQuery();
+                  }
+                  else {
+                    alertify.alert('Filter By Selection Error', res.data.result);
+                  }
+                }
+              );
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (e.readyState == 0) {
+                    alertify.alert('Filter By Selection Error',
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  alertify.alert('Filter By Selection Error', msg);
+                }, 10
+              );
+            }
+          });
+        },
+
+        // This function will exclude the filter by selection.
+        _exclude_filter: function () {
+           var self = this,
+              data = {}, grid, active_column, column_info, _values;
+
+         grid = self.slickgrid;
+         active_column = grid.getActiveCell();
+
+          // If no cell is selected then return from the function
+          if (_.isNull(active_column) || _.isUndefined(active_column))
+            return;
+
+          column_info = grid.getColumns()[active_column.cell]
+
+          // Fetch current row data from grid
+          _values = grid.getDataItem(active_column.row, active_column.cell)
+          if (_.isNull(_values) || _.isUndefined(_values))
+            return;
+
+          // Add column position and it's value to data
+          data[column_info.field] = _values[column_info.field] || '';
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Applying the new filter...")
+          );
+
+          // Make ajax call to exclude the filter by selection.
+          $.ajax({
+            url: url_for('sqleditor.exclusive_filter', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (res.data.status) {
+                    // Refresh the sql grid
+                    self.executeQuery();
+                  }
+                  else {
+                    alertify.alert('Filter Exclude Selection Error', res.data.result);
+                  }
+                }, 10
+              );
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+
+              setTimeout(
+                function() {
+                  if (e.readyState == 0) {
+                    alertify.alert('Filter Exclude Selection Error',
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  alertify.alert('Filter Exclude Selection Error', msg);
+                }, 10
+              );
+            }
+          });
+        },
+
+        // This function will remove the filter.
+        _remove_filter: function () {
+          var self = this;
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Removing the filter...")
+          );
+
+          // Make ajax call to exclude the filter by selection.
+          $.ajax({
+            url: url_for('sqleditor.remove_filter', {'trans_id': self.transId}),
+            method: 'POST',
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (res.data.status) {
+                    // Refresh the sql grid
+                    self.executeQuery();
+                  }
+                  else {
+                    alertify.alert('Remove Filter Error', res.data.result);
+                  }
+                }
+              );
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (e.readyState == 0) {
+                    alertify.alert('Remove Filter Error',
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  alertify.alert('Remove Filter Error', msg);
+                }
+              );
+            }
+          });
+        },
+
+        // This function will apply the filter.
+        _apply_filter: function() {
+          var self = this,
+              sql = self.gridView.filter_obj.getValue();
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Applying the filter...")
+          );
+
+          // Make ajax call to include the filter by selection
+          $.ajax({
+            url: url_for('sqleditor.apply_filter', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(sql),
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (res.data.status) {
+                    $('#filter').addClass('hidden');
+                    $('#editor-panel').removeClass('sql-editor-busy-fetching');
+                    // Refresh the sql grid
+                    self.executeQuery();
+                  }
+                  else {
+                    alertify.alert('Apply Filter Error',res.data.result);
+                  }
+                }, 10
+              );
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (e.readyState == 0) {
+                    alertify.alert('Apply Filter Error',
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  alertify.alert('Apply Filter Error', msg);
+                }, 10
+              );
+            }
+          });
+        },
+
+        // This function will copy the selected row.
+        _copy_row: copyData,
+
+        // This function will paste the selected row.
+        _paste_row: function() {
+          var self = this, col_info = {},
+            grid = self.slickgrid,
+            dataView = grid.getData(),
+            data = dataView.getItems(),
+            count = dataView.getLength(),
+            rows = grid.getSelectedRows().sort(
+              function (a, b) { return a - b; }
+            ),
+            copied_rows = rows.map(function (rowIndex) {
+              return data[rowIndex];
+            });
+
+            rows = rows.length == 0 ? self.last_copied_rows : rows
+
+            self.last_copied_rows = rows;
+
+            // If there are rows to paste?
+            if(copied_rows.length > 0) {
+              // Enable save button so that user can
+              // save newly pasted rows on server
+              $("#btn-save").prop('disabled', false);
+
+              var arr_to_object = function (arr) {
+                var obj = {},
+                  count = typeof(arr) == 'object' ?
+                            Object.keys(arr).length: arr.length
+
+                _.each(arr, function(val, i){
+                  if (arr[i] !== undefined) {
+                    if(_.isObject(arr[i])) {
+                      obj[String(i)] = JSON.stringify(arr[i]);
+                    } else {
+                      obj[String(i)] = arr[i];
+                    }
+                  }
+                });
+                return obj;
+              };
+
+              // Generate Unique key for each pasted row(s)
+              // Convert array values to object to send to server
+              // Add flag is_row_copied to handle [default] and [null]
+              // for copied rows.
+              // Add index of copied row into temp_new_rows
+              // Trigger grid.onAddNewRow when a row is copied
+              // Reset selection
+
+              dataView.beginUpdate();
+              _.each(copied_rows, function(row) {
+                  var new_row = arr_to_object(row),
+                  _key = (self.gridView.client_primary_key_counter++).toString();
+                  new_row.is_row_copied = true;
+                  self.temp_new_rows.push(count);
+                  new_row[self.client_primary_key] = _key;
+                  dataView.addItem(new_row);
+                  self.data_store.added[_key] = {'err': false, 'data': new_row};
+                  self.data_store.added_index[count] = _key;
+                  count++;
+              });
+              dataView.endUpdate();
+              grid.updateRowCount();
+              // Pasted row/s always append so bring last row in view port.
+              grid.scrollRowIntoView(dataView.getLength());
+              grid.setSelectedRows([]);
+            }
+        },
+
+        // This function will set the limit for SQL query
+        _set_limit: function() {
+          var self = this,
+              limit = parseInt($(".limit").val());
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Setting the limit on the result...")
+          );
+          // Make ajax call to change the limit
+          $.ajax({
+            url: url_for('sqleditor.set_limit', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(limit),
+            success: function(res) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (res.data.status) {
+                    // Refresh the sql grid
+                    self.executeQuery();
+                  }
+                  else
+                    alertify.alert('Change limit Error', res.data.result);
+                }, 10
+              );
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              setTimeout(
+                function() {
+                  if (e.readyState == 0) {
+                    alertify.alert('Change limit Error',
+                      gettext("Not connected to the server or the connection to the server has been closed.")
+                    );
+                    return;
+                  }
+
+                  var msg = e.responseText;
+                  if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                    msg = e.responseJSON.errormsg;
+
+                  alertify.alert('Change limit Error', msg);
+                }, 10
+              );
+            }
+          });
+        },
+
+        // This function is used to enable/disable buttons
+        disable_tool_buttons: function(disabled) {
+          $("#btn-clear").prop('disabled', disabled);
+          $("#btn-query-dropdown").prop('disabled', disabled);
+          $("#btn-edit-dropdown").prop('disabled', disabled);
+          $("#btn-edit").prop('disabled', disabled);
+          $('#btn-load-file').prop('disabled', disabled);
+        },
+
+        // This function will fetch the sql query from the text box
+        // and execute the query.
+        _execute: function (explain_prefix) {
+          var self = this,
+              sql = '',
+              history_msg = '';
+
+          self.has_more_rows = false;
+          self.fetching_rows = false;
+
+          /* If code is selected in the code mirror then execute
+           * the selected part else execute the complete code.
+           */
+          var selected_code = self.gridView.query_tool_obj.getSelection();
+          if (selected_code.length > 0)
+            sql = selected_code;
+          else
+            sql = self.gridView.query_tool_obj.getValue();
+
+          // If it is an empty query, do nothing.
+          if (sql.length <= 0) return;
+
+          self.trigger(
+            'pgadmin-sqleditor:loading-icon:show',
+            gettext("Initializing the query execution!")
+          );
+
+          $("#btn-flash").prop('disabled', true);
+
+          if (explain_prefix != undefined &&
+                !S.startsWith(sql.trim().toUpperCase(), "EXPLAIN")) {
+            sql = explain_prefix + ' ' + sql;
+          }
+
+          self.query_start_time = new Date();
+          self.query = sql;
+          self.rows_affected = 0;
+          self._init_polling_flags();
+          self.disable_tool_buttons(true);
+          $("#btn-cancel-query").prop('disabled', false);
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_start', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(sql),
+            success: function(res) {
+              // Remove marker
+              if (self.gridView.marker) {
+                self.gridView.marker.clear();
+                delete self.gridView.marker;
+                self.gridView.marker = null;
+
+                // Remove already existing marker
+                self.gridView.query_tool_obj.removeLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
+              }
+
+              if (res.data.status) {
+                self.trigger(
+                  'pgadmin-sqleditor:loading-icon:message',
+                  gettext("Waiting for the query execution to complete...")
+                );
+
+                self.can_edit = res.data.can_edit;
+                self.can_filter = res.data.can_filter;
+                self.info_notifier_timeout = res.data.info_notifier_timeout;
+
+                // If status is True then poll the result.
+                self._poll();
+              }
+              else {
+                self.trigger('pgadmin-sqleditor:loading-icon:hide');
+                self.disable_tool_buttons(false);
+                $("#btn-cancel-query").prop('disabled', true);
+                self.update_msg_history(false, res.data.result);
+
+                // Highlight the error in the sql panel
+                self._highlight_error(res.data.result);
+              }
+            },
+            error: function(e) {
+              self.trigger('pgadmin-sqleditor:loading-icon:hide');
+              self.disable_tool_buttons(false);
+              $("#btn-cancel-query").prop('disabled', true);
+
+              if (e.readyState == 0) {
+                self.update_msg_history(false,
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+                );
+                return;
+              }
+
+              var msg = e.responseText;
+              if (e.responseJSON != undefined &&
+                  e.responseJSON.errormsg != undefined)
+                msg = e.responseJSON.errormsg;
+
+              self.update_msg_history(false, msg);
+            }
+          });
+        },
+
+        /* This function is used to highlight the error line and
+         * underlining for the error word.
+         */
+        _highlight_error: function(result) {
+          var self = this,
+            error_line_no = 0,
+            start_marker = 0,
+            end_marker = 0,
+            selected_line_no = 0;
+
+          // Remove already existing marker
+          self.gridView.query_tool_obj.removeLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
+
+          // In case of selection we need to find the actual line no
+          if (self.gridView.query_tool_obj.getSelection().length > 0)
+            selected_line_no = self.gridView.query_tool_obj.getCursor(true).line;
+
+          // Fetch the LINE string using regex from the result
+          var line = /LINE (\d+)/.exec(result),
+            // Fetch the Character string using regex from the result
+            char = /Character: (\d+)/.exec(result);
+
+          // If line and character is null then no need to mark
+          if (line != null && char != null) {
+            error_line_no = self.marked_line_no = (parseInt(line[1]) - 1) + selected_line_no;
+            var error_char_no = (parseInt(char[1]) - 1);
+
+            /* We need to loop through each line till the error line and
+             * count the total no of character to figure out the actual
+             * starting/ending marker point for the individual line. We
+             * have also added 1 per line for the "\n" character.
+             */
+            var prev_line_chars = 0;
+            var loop_index = selected_line_no > 0 ? selected_line_no : 0;
+            for (var i = loop_index; i < error_line_no; i++)
+              prev_line_chars += self.gridView.query_tool_obj.getLine(i).length + 1;
+
+            /* Marker starting point for the individual line is
+             * equal to error character index minus total no of
+             * character till the error line starts.
+             */
+            start_marker = error_char_no - prev_line_chars;
+
+            // Find the next space from the character or end of line
+            var error_line = self.gridView.query_tool_obj.getLine(error_line_no);
+            end_marker = error_line.indexOf(' ', start_marker);
+            if (end_marker < 0)
+              end_marker = error_line.length;
+
+            // Mark the error text
+            self.gridView.marker = self.gridView.query_tool_obj.markText(
+              {line: error_line_no, ch: start_marker},
+              {line: error_line_no, ch: end_marker},
+              {className: "sql-editor-mark"}
+            );
+
+            self.gridView.query_tool_obj.addLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
+          }
+        },
+
+        // This function will cancel the running query.
+        _cancel_query: function() {
+          var self = this;
+
+          $("#btn-cancel-query").prop('disabled', true);
+          $.ajax({
+            url: url_for('sqleditor.cancel_transaction', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            success: function(res) {
+              if (res.data.status) {
+                self.disable_tool_buttons(false);
+              }
+              else {
+                self.disable_tool_buttons(false);
+                alertify.alert('Cancel Query Error', res.data.result);
+              }
+            },
+            error: function(e) {
+              self.disable_tool_buttons(false);
+
+              if (e.readyState == 0) {
+                alertify.alert('Cancel Query Error',
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+                );
+                return;
+              }
+
+              var msg = e.responseText;
+              if (e.responseJSON != undefined &&
+                  e.responseJSON.errormsg != undefined)
+                msg = e.responseJSON.errormsg;
+
+              alertify.alert('Cancel Query Error', msg);
+            }
+          });
+        },
+
+        // This function will download the grid data as CSV file.
+        download: function() {
+          var self = this,
+          selected_code = self.gridView.query_tool_obj.getSelection(),
+          sql = "";
+
+          if (selected_code.length > 0)
+            sql = selected_code;
+          else
+            sql = self.gridView.query_tool_obj.getValue();
+
+          // If it is an empty query, do nothing.
+          if (sql.length <= 0) return;
+
+          /* If download is from view data then file name should be
+           * the object name for which data is to be displayed.
+           */
+          if (!self.is_query_tool) {
+            $.ajax({
+              url: url_for('sqleditor.get_object_name', {'trans_id': self.transId}),
+              method: 'GET',
+              success: function(res) {
+                if (res.data.status) {
+                  filename = res.data.result + '.csv';
+                  self._trigger_csv_download(sql, filename);
+                 }
+              },
+              error: function(e) {
+                if (e.readyState == 0) {
+                  alertify.alert('Get Object Name Error',
+                    gettext("Not connected to the server or the connection to the server has been closed.")
+                  );
+                  return;
+                }
+
+                var msg = e.responseText;
+                if (e.responseJSON != undefined &&
+                    e.responseJSON.errormsg != undefined)
+                  msg = e.responseJSON.errormsg;
+
+                alertify.alert('Get Object Name Error', msg);
+              }
+            });
+          } else {
+            var cur_time = new Date();
+            var filename = 'data-' + cur_time.getTime() + '.csv';
+            self._trigger_csv_download(sql, filename);
+          }
+        },
+        // Trigger query result download to csv.
+        _trigger_csv_download: function(query, filename) {
+          var self = this,
+            link = $(this.container).find("#download-csv"),
+            url = url_for('sqleditor.query_tool_download', {'trans_id': self.transId});
+
+          url +="?" + $.param({query:query, filename:filename});
+          link.attr("src", url);
+        },
+
+        _auto_rollback: function() {
+          var self = this,
+              auto_rollback = true;
+
+          if ($('.auto-rollback').hasClass('visibility-hidden') === true)
+            $('.auto-rollback').removeClass('visibility-hidden');
+          else {
+            $('.auto-rollback').addClass('visibility-hidden');
+            auto_rollback = false;
+          }
+
+          // Make ajax call to change the limit
+          $.ajax({
+            url: url_for('sqleditor.auto_rollback', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(auto_rollback),
+            success: function(res) {
+              if (!res.data.status)
+                alertify.alert('Auto Rollback Error', res.data.result);
+            },
+            error: function(e) {
+              if (e.readyState == 0) {
+                alertify.alert('Auto Rollback Error',
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+                );
+                return;
+              }
+
+              var msg = e.responseText;
+              if (e.responseJSON != undefined &&
+                  e.responseJSON.errormsg != undefined)
+                msg = e.responseJSON.errormsg;
+
+              alertify.alert('Auto Rollback Error', msg);
+            }
+          });
+        },
+
+        _auto_commit: function() {
+          var self = this,
+              auto_commit = true;
+
+          if ($('.auto-commit').hasClass('visibility-hidden') === true)
+            $('.auto-commit').removeClass('visibility-hidden');
+          else {
+            $('.auto-commit').addClass('visibility-hidden');
+            auto_commit = false;
+          }
+
+          // Make ajax call to change the limit
+          $.ajax({
+            url: url_for('sqleditor.auto_commit', {'trans_id': self.transId}),
+            method: 'POST',
+            contentType: "application/json",
+            data: JSON.stringify(auto_commit),
+            success: function(res) {
+              if (!res.data.status)
+                alertify.alert('Auto Commit Error', res.data.result);
+            },
+            error: function(e) {
+              if (e.readyState == 0) {
+                alertify.alert('Auto Commit Error',
+                  gettext("Not connected to the server or the connection to the server has been closed.")
+                );
+                return;
+              }
+
+              var msg = e.responseText;
+              if (e.responseJSON != undefined &&
+                  e.responseJSON.errormsg != undefined)
+                msg = e.responseJSON.errormsg;
+
+              alertify.alert('Auto Commit Error', msg);
+            }
+          });
+        },
+
+        // This function will
+        explain: function() {
+          var self = this;
+          var verbose = $('.explain-verbose').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+          var costs = $('.explain-costs').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+
+          // No need to check for buffers and timing option value in explain
+          var explain_query = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE %s, COSTS %s, BUFFERS OFF, TIMING OFF) ';
+          explain_query = S(explain_query).sprintf(verbose, costs).value();
+          self._execute(explain_query);
+        },
+
+        // This function will
+        explainAnalyze: function() {
+          var self = this;
+          var verbose = $('.explain-verbose').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+          var costs = $('.explain-costs').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+          var buffers = $('.explain-buffers').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+          var timing = $('.explain-timing').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+
+          var explain_query = 'Explain (FORMAT JSON, ANALYZE ON, VERBOSE %s, COSTS %s, BUFFERS %s, TIMING %s) ';
+          explain_query = S(explain_query).sprintf(verbose, costs, buffers, timing).value();
+          self._execute(explain_query);
+        },
+
+        // This function will toggle "verbose" option in explain
+        _explain_verbose: function() {
+          var self = this;
+          if ($('.explain-verbose').hasClass('visibility-hidden') === true) {
+            $('.explain-verbose').removeClass('visibility-hidden');
+            self.explain_verbose = true;
+          }
+          else {
+            $('.explain-verbose').addClass('visibility-hidden');
+            self.explain_verbose = false;
+          }
+
+          // Set this option in preferences
+          var data = {
+            'explain_verbose': self.explain_verbose
+          };
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_preferences', {'trans_id': self.transId}),
+            method: 'PUT',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              if(res.success == undefined || !res.success) {
+                alertify.alert('Explain options error',
+                  gettext("Error occurred while setting verbose option in explain")
+                );
+              }
+            },
+            error: function(e) {
+              alertify.alert('Explain options error',
+                gettext("Error occurred while setting verbose option in explain")
+              );
+              return;
+            }
+          });
+        },
+
+        // This function will toggle "costs" option in explain
+        _explain_costs: function() {
+          var self = this;
+          if ($('.explain-costs').hasClass('visibility-hidden') === true) {
+            $('.explain-costs').removeClass('visibility-hidden');
+            self.explain_costs = true;
+          }
+          else {
+            $('.explain-costs').addClass('visibility-hidden');
+            self.explain_costs = false;
+          }
+
+          // Set this option in preferences
+          var data = {
+            'explain_costs': self.explain_costs
+          };
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_preferences', {'trans_id': self.transId}),
+            method: 'PUT',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              if(res.success == undefined || !res.success) {
+                alertify.alert('Explain options error',
+                  gettext("Error occurred while setting costs option in explain")
+                );
+              }
+            },
+            error: function(e) {
+              alertify.alert('Explain options error',
+                gettext("Error occurred while setting costs option in explain")
+              );
+            }
+          });
+        },
+
+        // This function will toggle "buffers" option in explain
+        _explain_buffers: function() {
+          var self = this;
+          if ($('.explain-buffers').hasClass('visibility-hidden') === true) {
+            $('.explain-buffers').removeClass('visibility-hidden');
+            self.explain_buffers = true;
+          }
+          else {
+            $('.explain-buffers').addClass('visibility-hidden');
+            self.explain_buffers = false;
+          }
+
+          // Set this option in preferences
+          var data = {
+            'explain_buffers': self.explain_buffers
+          };
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_preferences', {'trans_id': self.transId}),
+            method: 'PUT',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              if(res.success == undefined || !res.success) {
+                alertify.alert('Explain options error',
+                  gettext("Error occurred while setting buffers option in explain")
+                );
+              }
+            },
+            error: function(e) {
+              alertify.alert('Explain options error',
+                gettext("Error occurred while setting buffers option in explain")
+              );
+            }
+          });
+        },
+
+        // This function will toggle "timing" option in explain
+        _explain_timing: function() {
+          var self = this;
+          if ($('.explain-timing').hasClass('visibility-hidden') === true) {
+            $('.explain-timing').removeClass('visibility-hidden');
+            self.explain_timing = true;
+          }
+          else {
+            $('.explain-timing').addClass('visibility-hidden');
+            self.explain_timing = false;
+          }
+          // Set this option in preferences
+          var data = {
+            'explain_timing': self.explain_timing
+          };
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_preferences', {'trans_id': self.transId}),
+            method: 'PUT',
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(res) {
+              if(res.success == undefined || !res.success) {
+                alertify.alert('Explain options error',
+                  gettext("Error occurred while setting timing option in explain")
+                );
+              }
+            },
+            error: function(e) {
+              alertify.alert('Explain options error',
+                gettext("Error occurred while setting timing option in explain")
+              );
+            }
+          });
+        },
+
+        /*
+         * This function will comment code (Wrapper function)
+         */
+        commentLineCode: function() {
+          this._toggle_comment_code('comment_line');
+        },
+
+        /*
+         * This function will uncomment code (Wrapper function)
+         */
+        uncommentLineCode: function() {
+          this._toggle_comment_code('uncomment_line');
+        },
+
+        /*
+         * This function will comment/uncomment code (Wrapper function)
+         */
+        commentBlockCode: function() {
+          this._toggle_comment_code('block');
+        },
+
+        /*
+         * This function will comment/uncomment code (Main function)
+         */
+        _toggle_comment_code: function(of_type) {
+          var self = this, editor = self.gridView.query_tool_obj,
+          selected_code = editor.getSelection(),
+          sql = selected_code.length > 0 ? selected_code : editor.getValue();
+          // If it is an empty query, do nothing.
+          if (sql.length <= 0) return;
+
+          // Find the code selection range
+          var range = {
+            from: editor.getCursor(true),
+            to: editor.getCursor(false)
+          },
+          option = { lineComment: '--' };
+
+          if(of_type == 'comment_line') {
+            // Comment line
+            editor.lineComment(range.from, range.to, option);
+          } else if(of_type == 'uncomment_line') {
+            // Uncomment line
+            editor.uncomment(range.from, range.to, option);
+          } else if(of_type == 'block') {
+            editor.toggleComment(range.from, range.to);
+          }
+        },
+
+        /*
+         * This function will indent selected code
+         */
+        _indent_selected_code: function() {
+          var self = this, editor = self.gridView.query_tool_obj;
+          editor.execCommand("indentMore");
+        },
+
+        /*
+         * This function will unindent selected code
+         */
+        _unindent_selected_code: function() {
+          var self = this, editor = self.gridView.query_tool_obj;
+          editor.execCommand("indentLess");
+        },
+
+        isQueryRunning: function () {
+          return is_query_running;
+        },
+
+        /*
+         * This function get explain options and auto rollback/auto commit
+         * values from preferences
+         */
+        get_preferences: function() {
+          var self = this,
+              explain_verbose = false,
+              explain_costs = false,
+              explain_buffers = false,
+              explain_timing = false,
+              auto_commit = true,
+              auto_rollback = false,
+              updateUI = function() {
+                // Set Auto-commit and auto-rollback on query editor
+                if (auto_commit &&
+                  $('.auto-commit').hasClass('visibility-hidden') === true)
+                  $('.auto-commit').removeClass('visibility-hidden');
+                else {
+                  $('.auto-commit').addClass('visibility-hidden');
+                }
+                if (auto_rollback &&
+                  $('.auto-rollback').hasClass('visibility-hidden') === true)
+                  $('.auto-rollback').removeClass('visibility-hidden');
+                else {
+                  $('.auto-rollback').addClass('visibility-hidden');
+                }
+
+                // Set explain options on query editor
+                if (explain_verbose &&
+                  $('.explain-verbose').hasClass('visibility-hidden') === true)
+                  $('.explain-verbose').removeClass('visibility-hidden');
+                else {
+                  $('.explain-verbose').addClass('visibility-hidden');
+                }
+                if (explain_costs &&
+                  $('.explain-costs').hasClass('visibility-hidden') === true)
+                  $('.explain-costs').removeClass('visibility-hidden');
+                else {
+                  $('.explain-costs').addClass('visibility-hidden');
+                }
+                if (explain_buffers &&
+                  $('.explain-buffers').hasClass('visibility-hidden') === true)
+                  $('.explain-buffers').removeClass('visibility-hidden');
+                else {
+                  $('.explain-buffers').addClass('visibility-hidden');
+                }
+                if (explain_timing &&
+                  $('.explain-timing').hasClass('visibility-hidden') === true)
+                  $('.explain-timing').removeClass('visibility-hidden');
+                else {
+                  $('.explain-timing').addClass('visibility-hidden');
+                }
+              };
+
+          $.ajax({
+            url: url_for('sqleditor.query_tool_preferences', {'trans_id': self.transId}),
+            method: 'GET',
+            success: function(res) {
+              if (res.data) {
+                explain_verbose = res.data.explain_verbose;
+                explain_costs = res.data.explain_costs;
+                explain_buffers = res.data.explain_buffers;
+                explain_timing = res.data.explain_timing;
+                auto_commit = res.data.auto_commit;
+                auto_rollback = res.data.auto_rollback;
+
+                updateUI();
+              }
+            },
+            error: function(e) {
+              updateUI();
+              alertify.alert('Get Preferences error',
+                gettext("Error occurred while getting query tool options ")
+              );
+            }
+          });
+        },
+        close: function() {
+          var self= this;
+          _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(panel) {
+            if(panel.isVisible()) {
+              window.onbeforeunload = null;
+              panel.off(wcDocker.EVENT.CLOSING);
+              // remove col_size object on panel close
+              if (!_.isUndefined(self.col_size)) {
+                delete self.col_size;
+              }
+              window.top.pgAdmin.Browser.docker.removePanel(panel);
+            }
+          });
+        }
+      }
+    );
+
+    pgAdmin.SqlEditor = {
+      // This function is used to create and return the object of grid controller.
+      create: function(container) {
+        return new SqlEditorController(container);
+      },
+      jquery: $,
+      S: S
+    };
+
+    return pgAdmin.SqlEditor;
+  });
diff --git a/web/pgadmin/tools/templates/js/translations.js b/web/pgadmin/tools/templates/js/translations.js
index 71b53db4..407e0855 100644
--- a/web/pgadmin/tools/templates/js/translations.js
+++ b/web/pgadmin/tools/templates/js/translations.js
@@ -1,4 +1,4 @@
 define(function () {
   var translations = {{ translations|tojson }};
   return [];
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/tools/user_management/__init__.py b/web/pgadmin/tools/user_management/__init__.py
index 102e6955..9699602b 100644
--- a/web/pgadmin/tools/user_management/__init__.py
+++ b/web/pgadmin/tools/user_management/__init__.py
@@ -9,6 +9,7 @@
 
 """Implements pgAdmin4 User Management Utility"""
 
+import os
 import simplejson as json
 import re
 
@@ -49,8 +50,9 @@ class UserManagementModule(PgAdminModule):
         """
         return [{
             'name': 'pgadmin.tools.user_management',
-            'path': url_for('user_management.index') + 'user_management',
-            'when': None
+            'path': os.path.join(self.root_path, 'static/js/user_management'),
+            'when': None,
+            'is_template': False
         },{
             'name': 'pgadmin.user_management.current_user',
             'path': url_for('user_management.index') + 'current_user',
diff --git a/web/pgadmin/tools/user_management/static/js/user_management.js b/web/pgadmin/tools/user_management/static/js/user_management.js
new file mode 100644
index 00000000..3fe41a5a
--- /dev/null
+++ b/web/pgadmin/tools/user_management/static/js/user_management.js
@@ -0,0 +1,642 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify',
+  'pgadmin.browser', 'backbone', 'backgrid', 'backform', 'pgadmin.browser.node',
+  'sources/alerts/alertify_wrapper', 'pgadmin.user_management.current_user',
+  'backgrid.select.all', 'backgrid.filter'
+], function(
+  gettext, url_for, $, _, S, alertify, pgBrowser, Backbone, Backgrid, Backform,
+  pgNode, AlertifyWrapper, userInfo
+) {
+
+    // if module is already initialized, refer to that.
+    if (pgBrowser.UserManagement) {
+      return pgBrowser.UserManagement;
+    }
+
+    var USERURL = url_for('user_management.users'),
+        ROLEURL = url_for('user_management.roles'),
+        userFilter = function(collection) {
+          return (new Backgrid.Extension.ClientSideFilter({
+            collection: collection,
+            placeholder: _('Filter by email'),
+
+            // The model fields to search for matches
+            fields: ['email'],
+
+            // How long to wait after typing has stopped before searching can start
+            wait: 150
+          }));
+        }
+
+    pgBrowser.UserManagement  = {
+      init: function() {
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        return this;
+      },
+      // Callback to draw User Management Dialog.
+      show_users: function(action, item, params) {
+        if (!userInfo['is_admin']) return;
+        var Roles = [];
+
+        var UserModel = pgAdmin.Browser.Node.Model.extend({
+            idAttribute: 'id',
+            urlRoot: USERURL,
+            defaults: {
+              id: undefined,
+              email: undefined,
+              active: true,
+              role: undefined,
+              newPassword: undefined,
+              confirmPassword: undefined
+            },
+            schema: [
+            {
+              id: 'email', label: gettext('Email'), type: 'text',
+              cell: Backgrid.Extension.StringDepCell, cellHeaderClasses: 'width_percent_30',
+              deps: ['id'],
+              editable: function(m) {
+                if(m instanceof Backbone.Collection) {
+                  return false;
+                }
+                // Disable email edit for existing user.
+                if (m.isNew()){
+                    return true;
+                }
+                return false;
+              }
+            },{
+              id: 'role', label: gettext('Role'),
+              type: 'text', control: "Select2", cellHeaderClasses:'width_percent_20',
+              cell: 'select2', select2: {allowClear: false, openOnEnter: false},
+              options: function (controlOrCell) {
+                var options = [];
+
+                if( controlOrCell instanceof Backform.Control){
+                  // This is be backform select2 control
+                  _.each(Roles, function(role) {
+                    options.push({
+                      label: role.name,
+                      value: role.id.toString()}
+                    );
+                  });
+                } else {
+                  // This must be backgrid select2 cell
+                  _.each(Roles, function(role) {
+                    options.push([role.name, role.id.toString()]);
+                  });
+                }
+
+                return options;
+              },
+              editable: function(m) {
+                if(m instanceof Backbone.Collection) {
+                  return true;
+                }
+                if (m.get("id") == userInfo['user_id']){
+                    return false;
+                } else {
+                    return true;
+                }
+              }
+            },{
+              id: 'active', label: gettext('Active'),
+              type: 'switch', cell: 'switch', cellHeaderClasses:'width_percent_10',
+              options: { 'onText': 'Yes', 'offText': 'No'},
+              editable: function(m) {
+                if(m instanceof Backbone.Collection) {
+                  return true;
+                }
+                if (m.get("id") == userInfo['user_id']){
+                    return false;
+                } else {
+                    return true;
+                }
+              }
+            },{
+              id: 'newPassword', label: gettext('New password'),
+              type: 'password', disabled: false, control: 'input',
+              cell: 'password', cellHeaderClasses:'width_percent_20'
+            },{
+              id: 'confirmPassword', label: gettext('Confirm password'),
+              type: 'password', disabled: false, control: 'input',
+              cell: 'password', cellHeaderClasses:'width_percent_20'
+            }],
+            validate: function() {
+              var err = {},
+                  errmsg = null,
+                  changedAttrs = this.changed || {},
+                  email_filter = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
+
+              if (('email' in changedAttrs || !this.isNew()) && (_.isUndefined(this.get('email')) ||
+                    _.isNull(this.get('email')) ||
+                    String(this.get('email')).replace(/^\s+|\s+$/g, '') == '')) {
+                errmsg = gettext('Email address cannot be empty.');
+                this.errorModel.set('email', errmsg);
+                return errmsg;
+              } else if (!!this.get('email') && !email_filter.test(this.get('email'))) {
+
+                errmsg =  S(gettext("Invalid email address: %s.")).sprintf(
+                            this.get('email')
+                          ).value();
+                this.errorModel.set('email', errmsg);
+                return errmsg;
+              } else if (!!this.get('email') && this.collection.where({"email":this.get('email')}).length > 1) {
+
+                errmsg =  S(gettext("The email address %s already exists.")).sprintf(
+                            this.get('email')
+                          ).value();
+
+                this.errorModel.set('email', errmsg);
+                return errmsg;
+              } else {
+                this.errorModel.unset('email');
+              }
+
+              if ('role' in changedAttrs && (_.isUndefined(this.get('role')) ||
+                    _.isNull(this.get('role')) ||
+                    String(this.get('role')).replace(/^\s+|\s+$/g, '') == '')) {
+
+                errmsg =  S(gettext("Role cannot be empty for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                this.errorModel.set('role', errmsg);
+                return errmsg;
+              } else {
+                this.errorModel.unset('role');
+              }
+
+              if(this.isNew()){
+                // Password is compulsory for new user.
+                if ('newPassword' in changedAttrs && (_.isUndefined(this.get('newPassword')) ||
+                      _.isNull(this.get('newPassword')) ||
+                      this.get('newPassword') == '')) {
+
+                  errmsg =  S(gettext("Password cannot be empty for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('newPassword', errmsg);
+                  return errmsg;
+                } else if (!_.isUndefined(this.get('newPassword')) &&
+                  !_.isNull(this.get('newPassword')) &&
+                  this.get('newPassword').length < 6) {
+
+                  errmsg =  S(gettext("Password must be at least 6 characters for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('newPassword', errmsg);
+                  return errmsg;
+                } else {
+                  this.errorModel.unset('newPassword');
+                }
+
+                if ('confirmPassword' in changedAttrs && (_.isUndefined(this.get('confirmPassword')) ||
+                      _.isNull(this.get('confirmPassword')) ||
+                      this.get('confirmPassword') == '')) {
+
+                  errmsg =  S(gettext("Confirm Password cannot be empty for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('confirmPassword', errmsg);
+                  return errmsg;
+                } else {
+                  this.errorModel.unset('confirmPassword');
+                }
+
+                if(!!this.get('newPassword') && !!this.get('confirmPassword') &&
+                    this.get('newPassword') != this.get('confirmPassword')) {
+
+                  errmsg =  S(gettext("Passwords do not match for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('confirmPassword', errmsg);
+                  return errmsg;
+                } else {
+                  this.errorModel.unset('confirmPassword');
+                }
+
+              } else {
+                if ((_.isUndefined(this.get('newPassword')) || _.isNull(this.get('newPassword')) ||
+                      this.get('newPassword') == '') &&
+                      ((_.isUndefined(this.get('confirmPassword')) || _.isNull(this.get('confirmPassword')) ||
+                      this.get('confirmPassword') == ''))) {
+
+                   this.errorModel.unset('newPassword');
+                   if(this.get('newPassword') == ''){
+                    this.set({'newPassword': undefined})
+                   }
+
+                   this.errorModel.unset('confirmPassword');
+                   if(this.get('confirmPassword') == ''){
+                    this.set({'confirmPassword': undefined})
+                   }
+                } else if (!_.isUndefined(this.get('newPassword')) &&
+                    !_.isNull(this.get('newPassword')) &&
+                    !this.get('newPassword') == '' &&
+                    this.get('newPassword').length < 6) {
+
+                  errmsg =  S(gettext("Password must be at least 6 characters for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('newPassword', errmsg);
+                  return errmsg;
+                } else if (_.isUndefined(this.get('confirmPassword')) ||
+                      _.isNull(this.get('confirmPassword')) ||
+                      this.get('confirmPassword') == '') {
+
+                  errmsg =  S(gettext("Confirm Password cannot be empty for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('confirmPassword', errmsg);
+                  return errmsg;
+                } else if (!!this.get('newPassword') && !!this.get('confirmPassword') &&
+                          this.get('newPassword') != this.get('confirmPassword')) {
+
+                  errmsg =  S(gettext("Passwords do not match for user %s.")).sprintf(
+                            (this.get('email') || '')
+                          ).value();
+
+                  this.errorModel.set('confirmPassword', errmsg);
+                  return errmsg;
+                } else {
+                  this.errorModel.unset('newPassword');
+                  this.errorModel.unset('confirmPassword');
+                }
+              }
+              return null;
+            }
+          }),
+          gridSchema = Backform.generateGridColumnsFromModel(
+              null, UserModel, 'edit'),
+          deleteUserCell = Backgrid.Extension.DeleteCell.extend({
+            deleteRow: function(e) {
+              self = this;
+              e.preventDefault();
+
+              if (self.model.get("id") == userInfo['user_id']) {
+                alertify.alert(
+                  gettext('Cannot delete user.'),
+                  gettext('Cannot delete currently logged in user.'),
+                  function(){
+                    return true;
+                  }
+                );
+                return true;
+              }
+
+              // We will check if row is deletable or not
+              var canDeleteRow = (!_.isUndefined(this.column.get('canDeleteRow')) &&
+                                  _.isFunction(this.column.get('canDeleteRow')) ) ?
+                                   Backgrid.callByNeed(this.column.get('canDeleteRow'),
+                                    this.column, this.model) : true;
+              if (canDeleteRow) {
+                if(self.model.isNew()){
+                  self.model.destroy();
+                } else {
+                  alertify.confirm(
+                    'Delete user?',
+                    'Are you sure you wish to delete this user?',
+                    function(evt) {
+                      self.model.destroy({
+                        wait: true,
+                        success: function(res) {
+                          var alertifyWrapper = new AlertifyWrapper();
+                          alertifyWrapper.success(gettext('User deleted.'));
+                        },
+                        error: function(m, jqxhr) {
+                          var alertifyWrapper = new AlertifyWrapper();
+                          alertifyWrapper.error(gettext('Error during deleting user.'));
+                        }
+                      });
+                    },
+                    function(evt) {
+                      return true;
+                    }
+                  );
+                }
+              } else {
+                alertify.alert("This user cannot be deleted.",
+                  function(){
+                    return true;
+                  }
+                );
+              }
+            }
+          });
+
+          gridSchema.columns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: deleteUserCell,
+            editable: false, cell_priority: -1,
+            canDeleteRow: true
+          });
+
+        // Users Management dialog code here
+        if(!alertify.UserManagement) {
+          alertify.dialog('UserManagement' ,function factory() {
+            return {
+              main: function(title) {
+                this.set('title', title);
+              },
+              build: function() {
+                alertify.pgDialogBuild.apply(this)
+              },
+              setup:function() {
+                return {
+                  buttons: [{
+                    text: '', key: 112, className: 'btn btn-default pull-left fa fa-lg fa-question',
+                    attrs:{
+                      name:'dialog_help', type:'button', label: gettext('Users'),
+                      url: url_for(
+                        'help.static', {
+                          'filename': 'pgadmin_user.html'
+                        })
+                    }
+                  },{
+                    text: gettext('Close'), key: 27, className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button user_management_pg-alertify-button',
+                    attrs:{name:'close', type:'button'}
+                  }],
+                  // Set options for dialog
+                  options: {
+                    title: gettext('User Management'),
+                    //disable both padding and overflow control.
+                    padding : !1,
+                    overflow: !1,
+                    modal: false,
+                    resizable: true,
+                    maximizable: true,
+                    pinnable: false,
+                    closableByDimmer: false,
+                    closable: false
+                  }
+                };
+              },
+              hooks: {
+                // Triggered when the dialog is closed
+                onclose: function() {
+                  if (this.view) {
+                    // clear our backform model/view
+                    this.view.remove({data: true, internal: true, silent: true});
+                    this.$content.remove();
+                  }
+                }
+              },
+              prepare: function() {
+                var self = this,
+                  footerTpl = _.template([
+                    '<div class="pg-prop-footer">',
+                      '<div class="pg-prop-status-bar" style="visibility:hidden">',
+                      '</div>',
+                    '</div>'].join("\n")),
+                  $footer = $(footerTpl()),
+                  $statusBar = $footer.find('.pg-prop-status-bar'),
+                  UserRow = Backgrid.Row.extend({
+                    userInvalidColor: "lightYellow",
+
+                    userValidColor: "#fff",
+
+                    initialize: function() {
+                      Backgrid.Row.prototype.initialize.apply(this, arguments);
+                      this.listenTo(this.model, 'pgadmin:user:invalid', this.userInvalid);
+                      this.listenTo(this.model, 'pgadmin:user:valid', this.userValid);
+                    },
+                    userInvalid: function() {
+                      $(this.el).removeClass("new");
+                      this.el.style.backgroundColor = this.userInvalidColor;
+                    },
+                    userValid: function() {
+                      this.el.style.backgroundColor = this.userValidColor;
+                    }
+                  }),
+                  UserCollection = Backbone.Collection.extend({
+                    model: UserModel,
+                    url: USERURL,
+                    initialize: function() {
+                      Backbone.Collection.prototype.initialize.apply(this, arguments);
+                      var self = this;
+                      self.changedUser = null;
+                      self.invalidUsers = {};
+
+                      self.on('add', self.onModelAdd);
+                      self.on('remove', self.onModelRemove);
+                      self.on('pgadmin-session:model:invalid', function(msg, m, c) {
+                        self.invalidUsers[m.cid] = msg;
+                        m.trigger('pgadmin:user:invalid', m);
+                        $statusBar.html(msg).css("visibility", "visible");
+                      });
+                      self.on('pgadmin-session:model:valid', function(m, c) {
+                        delete self.invalidUsers[m.cid];
+                        m.trigger('pgadmin:user:valid', m);
+                        this.updateErrorMsg();
+                        this.saveUser(m);
+                      });
+                    },
+                    onModelAdd: function(m) {
+                      // Start tracking changes.
+                      m.startNewSession();
+                    },
+                    onModelRemove: function(m) {
+                      delete this.invalidUsers[m.cid];
+                      this.updateErrorMsg();
+                    },
+                    updateErrorMsg: function() {
+                      var self = this,
+                        msg = null;
+
+                      for (var key in self.invalidUsers) {
+                        msg = self.invalidUsers [key];
+                        if (msg) {
+                          break;
+                        }
+                      }
+
+                      if(msg){
+                        $statusBar.html(msg).css("visibility", "visible");
+                      } else {
+                        $statusBar.empty().css("visibility", "hidden");
+                      }
+                    },
+                    saveUser: function(m) {
+                      var d = m.toJSON(true);
+
+                      if(m.isNew() && (!m.get('email') || !m.get('role') ||
+                          !m.get('newPassword') || !m.get('confirmPassword') ||
+                          m.get('newPassword') != m.get('confirmPassword'))
+                      ) {
+                      // New user model is valid but partially filled so return without saving.
+                        return false;
+                      } else if (!m.isNew() && m.get('newPassword') != m.get('confirmPassword')) {
+                      // For old user password change is in progress and user model is valid but admin has not added
+                      // both the passwords so return without saving.
+                        return false;
+                      }
+
+                      if (m.sessChanged() && d && !_.isEmpty(d)) {
+                        m.stopSession();
+                        m.save({}, {
+                          attrs: d,
+                          wait: true,
+                          success: function(res) {
+                            // User created/updated on server now start new session for this user.
+                            m.set({'newPassword':undefined,
+                                   'confirmPassword':undefined});
+
+                            m.startNewSession();
+                            var alertifyWrapper = new AlertifyWrapper();
+                            alertifyWrapper.success(S(gettext("User '%s' saved.")).sprintf(
+                              m.get('email')
+                            ).value());
+                          },
+                          error: function(res, jqxhr) {
+                            m.startNewSession();
+                            var alertifyWrapper = new AlertifyWrapper();
+                            alertifyWrapper.error(
+                              S(gettext("Error saving user: '%s'")).sprintf(
+                                jqxhr.responseJSON.errormsg
+                              ).value()
+                            );
+                          }
+                        });
+                      }
+                    }
+                  }),
+                  userCollection = this.userCollection = new UserCollection(),
+                  header = [
+                    '<div class="subnode-header">',
+                    '  <button class="btn-sm btn-default add fa fa-plus" title="<%-add_title%>" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
+                    '  <div class="control-label search_users"></div>',
+                    '</div>',].join("\n"),
+                  headerTpl = _.template(header),
+                  data = {
+                    canAdd: true,
+                    add_title: gettext('Add new user')
+                  },
+                  $gridBody = $("<div></div>", {
+                    class: "user_container"
+                  });
+
+                $.ajax({
+                  url: ROLEURL,
+                  method: 'GET',
+                  async: false,
+                  success: function(res) {
+                    Roles = res
+                  },
+                  error: function(e) {
+                    setTimeout(function() {
+                      alertify.alert(gettext('Cannot load user roles.'));
+                    },100);
+                  }
+                });
+
+                var view = this.view = new Backgrid.Grid({
+                  row: UserRow,
+                  columns: gridSchema.columns,
+                  collection: userCollection,
+                  className: "backgrid table-bordered"
+                });
+
+                $gridBody.append(view.render().$el[0]);
+
+                this.$content = $("<div class='user_management object subnode'></div>").append(
+                    headerTpl(data)).append($gridBody
+                    ).append($footer);
+
+                $(this.elements.body.childNodes[0]).addClass(
+                  'alertify_tools_dialog_backgrid_properties');
+
+                this.elements.content.appendChild(this.$content[0]);
+
+                // Render Search Filter
+                $('.search_users').append(
+                  userFilter(userCollection).render().el);
+
+                userCollection.fetch();
+
+                this.$content.find('button.add').first().click(function(e) {
+                  e.preventDefault();
+                  var canAddRow = true;
+
+                  if (canAddRow) {
+                      // There should be only one empty row.
+
+                      var isEmpty = false,
+                        unsavedModel = null;
+
+                      userCollection.each(function(model) {
+                        if(!isEmpty) {
+                          isEmpty = model.isNew();
+                          unsavedModel = model;
+                        }
+                      });
+                      if(isEmpty) {
+                        var idx = userCollection.indexOf(unsavedModel),
+                          row = view.body.rows[idx].$el;
+
+                        row.addClass("new");
+                        $(row).pgMakeVisible('backform-tab');
+                        return false;
+                      }
+
+                      $(view.body.$el.find($("tr.new"))).removeClass("new")
+                      var m = new (UserModel) (null, {
+                        handler: userCollection,
+                        top: userCollection,
+                        collection: userCollection
+                      });
+                      userCollection.add(m);
+
+                      var idx = userCollection.indexOf(m),
+                          newRow = view.body.rows[idx].$el;
+
+                      newRow.addClass("new");
+                      $(newRow).pgMakeVisible('backform-tab');
+                      return false;
+                  }
+                });
+              },
+              callback: function(e) {
+                if (e.button.element.name == "dialog_help") {
+                  e.cancel = true;
+                  pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                    null, null, e.button.element.getAttribute('label'));
+                  return;
+                }
+                if (e.button.element.name == "close") {
+                  var self = this;
+                  if (!_.all(this.userCollection.pluck('id')) || !_.isEmpty(this.userCollection.invalidUsers)) {
+                    e.cancel = true;
+                    alertify.confirm(
+                      gettext('Discard unsaved changes?'),
+                      gettext('Are you sure you want to close the dialog? Any unsaved changes will be lost.'),
+                      function(e) {
+                        self.close();
+                        return true;
+                      },
+                      function(e) {
+                        // Do nothing.
+                        return true;
+                      }
+                    );
+                  }
+                }
+              }
+          };
+       });
+      }
+        alertify.UserManagement(true).resizeTo('680px','400px');
+     }
+
+    };
+    return pgBrowser.UserManagement;
+  });
