diff --git a/web/pgadmin/about/templates/about/about.js b/web/pgadmin/about/templates/about/about.js deleted file mode 100644 index a746d0f5..00000000 --- a/web/pgadmin/about/templates/about/about.js +++ /dev/null @@ -1,54 +0,0 @@ -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/server_groups/static/js/server-group.js b/web/pgadmin/browser/server_groups/static/js/server-group.js deleted file mode 100644 index eb551244..00000000 --- a/web/pgadmin/browser/server_groups/static/js/server-group.js +++ /dev/null @@ -1,82 +0,0 @@ -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/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js deleted file mode 100644 index c3534185..00000000 --- a/web/pgadmin/browser/templates/browser/js/browser.js +++ /dev/null @@ -1,2004 +0,0 @@ -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 -) { - - // 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: '
' - }), - // 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: '
' + select_object_msg + '
', - 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: '
' + select_object_msg + '
', - 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: '' - }), - // 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: '
' + select_object_msg + '
', - 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: '
' + select_object_msg + '
', - 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: $(''), - 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 = $('
'), - 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) + - ' (' + _old['collection_count'] + ')' - } - } - - 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/templates/browser/js/collection.js b/web/pgadmin/browser/templates/browser/js/collection.js deleted file mode 100644 index 72570c75..00000000 --- a/web/pgadmin/browser/templates/browser/js/collection.js +++ /dev/null @@ -1,176 +0,0 @@ -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 = $('
') - .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/templates/browser/js/error.js b/web/pgadmin/browser/templates/browser/js/error.js deleted file mode 100644 index 7f845af6..00000000 --- a/web/pgadmin/browser/templates/browser/js/error.js +++ /dev/null @@ -1,45 +0,0 @@ -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 = '
\ -
\ - \ -
\ -
' + unescape(message) + '
\ -
\ -
'; - - if (info != null && info != '') { - text += '
\ - \ -
\ -
' + unescape(info) + '
\ -
\ -
\ -
' - } - - text += ''; - alertify.alert( - title, - text - ) - }, - }); - - return pgAdmin.Browser.report_error; -}); diff --git a/web/pgadmin/browser/templates/browser/js/node.js b/web/pgadmin/browser/templates/browser/js/node.js deleted file mode 100644 index 670498da..00000000 --- a/web/pgadmin/browser/templates/browser/js/node.js +++ /dev/null @@ -1,1572 +0,0 @@ -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 (_.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 = '\ - '; - 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 = '
'+ - gettext("Retrieving data from the server...") + '
', - $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: '
' + gettext('Please wait while we fetch information about the node from the server!') + '
', - 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); - - 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) + ' (' + pData.collection_count + ')' - ) - } - ); - } - }, - // 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) + ' (' + pData.collection_count + ')' - ) - } - ); - } - - 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 = $('
') - .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 = $('
').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 = - $('
').addClass( - 'pg-prop-btn-group' - ), - // Template used for creating a button - tmpl = _.template([ - '' - ].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}); - view = null; - // Reset the data object - j.data('obj-view', null); - } - // Make sure the HTML element is empty. - j.empty(); - that.header = $('
').addClass( - 'pg-prop-header' - ).appendTo(j); - that.footer = $('
').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}); - view = null; - // Reset the data object - j.data('obj-view', null); - } - // Make sure the HTML element is empty. - j.empty(); - - that.header = $('
').addClass( - 'pg-prop-header' - ).appendTo(j) - that.footer = $('
').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/templates/dashboard/js/dashboard.js b/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js deleted file mode 100644 index 1eee1da1..00000000 --- a/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js +++ /dev/null @@ -1,1120 +0,0 @@ -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( - "" - ); - 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( - '' - ); - } - }); - - // 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( - '' - ); - } - }); - $(dashboardPanel).data('server_status', true); - } - - } - else { - $(div).empty(); - $(div).html( - '' - ); - $(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( - '' - ); - - // 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( - '' - ); - - // 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/file_manager/templates/file_manager/js/file_manager.js b/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js deleted file mode 100644 index 656c3b35..00000000 --- a/web/pgadmin/misc/file_manager/templates/file_manager/js/file_manager.js +++ /dev/null @@ -1,680 +0,0 @@ -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 = $("
"), - 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("
"); - 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 = $("
"); - 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("
"); - 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 = $("
"), - 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("
"); - 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 = $("
"), - 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("
"); - 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/static/explain/vendor/snap.svg/snap.svg-min.js b/web/pgadmin/misc/static/explain/vendor/snap.svg/snap.svg-min.js deleted file mode 100644 index 6567d199..00000000 --- a/web/pgadmin/misc/static/explain/vendor/snap.svg/snap.svg-min.js +++ /dev/null @@ -1,21 +0,0 @@ -// Snap.svg 0.4.1 -// -// Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// build: 2015-04-13 - -!function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g=/\s*,\s*/,h="*",i=function(a,b){return a-b},j={n:{}},k=function(){for(var a=0,b=this.length;b>a;a++)if("undefined"!=typeof this[a])return this[a]},l=function(){for(var a=this.length;--a;)if("undefined"!=typeof this[a])return this[a]},m=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=m.listeners(a),j=0,n=[],o={},p=[],q=b;p.firstDefined=k,p.lastDefined=l,b=a,c=0;for(var r=0,s=h.length;s>r;r++)"zIndex"in h[r]&&(n.push(h[r].zIndex),h[r].zIndex<0&&(o[h[r].zIndex]=h[r]));for(n.sort(i);n[j]<0;)if(e=o[n[j++]],p.push(e.apply(d,g)),c)return c=f,p;for(r=0;s>r;r++)if(e=h[r],"zIndex"in e)if(e.zIndex==n[j]){if(p.push(e.apply(d,g)),c)break;do if(j++,e=o[n[j]],e&&p.push(e.apply(d,g)),c)break;while(e)}else o[e.zIndex]=e;else if(p.push(e.apply(d,g)),c)break;return c=f,b=q,p};m._events=j,m.listeners=function(a){var b,c,d,e,g,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,g=m.length;g>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[h]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},m.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(g),d=0,e=c.length;e>d;d++)!function(a){for(var c,d=a.split(f),e=j,g=0,h=d.length;h>g;g++)e=e.n,e=e.hasOwnProperty(d[g])&&e[d[g]]||(e[d[g]]={n:{}});for(e.f=e.f||[],g=0,h=e.f.length;h>g;g++)if(e.f[g]==b){c=!0;break}!c&&e.f.push(b)}(c[d]);return function(a){+a==+a&&(b.zIndex=+a)}},m.f=function(a){var b=[].slice.call(arguments,1);return function(){m.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},m.stop=function(){c=1},m.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},m.nts=function(){return b.split(f)},m.off=m.unbind=function(a,b){if(!a)return void(m._events=j={n:{}});var c=a.split(g);if(c.length>1)for(var d=0,i=c.length;i>d;d++)m.off(c[d],b);else{c=a.split(f);var k,l,n,d,i,o,p,q=[j];for(d=0,i=c.length;i>d;d++)for(o=0;od;d++)for(k=q[d];k.n;){if(b){if(k.f){for(o=0,p=k.f.length;p>o;o++)if(k.f[o]==b){k.f.splice(o,1);break}!k.f.length&&delete k.f}for(l in k.n)if(k.n[e](l)&&k.n[l].f){var r=k.n[l].f;for(o=0,p=r.length;p>o;o++)if(r[o]==b){r.splice(o,1);break}!r.length&&delete k.n[l].f}}else{delete k.f;for(l in k.n)k.n[e](l)&&k.n[l].f&&delete k.n[l].f}k=k.n}}},m.once=function(a,b){var c=function(){return m.unbind(a,c),b.apply(this,arguments)};return m.on(a,c)},m.version=d,m.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=m:"function"==typeof define&&define.amd?define("eve",[],function(){return m}):a.eve=m}(this),function(a,b){if("function"==typeof define&&define.amd)define(["eve"],function(c){return b(a,c)});else if("undefined"!=typeof exports){var c=require("eve");module.exports=b(a,c)}else b(a,a.eve)}(window||this,function(a,b){var c=function(b){var c={},d=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},e=Array.isArray||function(a){return a instanceof Array||"[object Array]"==Object.prototype.toString.call(a)},f=0,g="M"+(+new Date).toString(36),h=function(){return g+(f++).toString(36)},i=Date.now||function(){return+new Date},j=function(a){var b=this;if(null==a)return b.s;var c=b.s-a;b.b+=b.dur*c,b.B+=b.dur*c,b.s=a},k=function(a){var b=this;return null==a?b.spd:void(b.spd=a)},l=function(a){var b=this;return null==a?b.dur:(b.s=b.s*a/b.dur,void(b.dur=a))},m=function(){var a=this;delete c[a.id],a.update(),b("mina.stop."+a.id,a)},n=function(){var a=this;a.pdif||(delete c[a.id],a.update(),a.pdif=a.get()-a.b)},o=function(){var a=this;a.pdif&&(a.b=a.get()-a.pdif,delete a.pdif,c[a.id]=a)},p=function(){var a,b=this;if(e(b.start)){a=[];for(var c=0,d=b.start.length;d>c;c++)a[c]=+b.start[c]+(b.end[c]-b.start[c])*b.easing(b.s)}else a=+b.start+(b.end-b.start)*b.easing(b.s);b.set(a)},q=function(){var a=0;for(var e in c)if(c.hasOwnProperty(e)){var f=c[e],g=f.get();a++,f.s=(g-f.b)/(f.dur/f.spd),f.s>=1&&(delete c[e],f.s=1,a--,function(a){setTimeout(function(){b("mina.finish."+a.id,a)})}(f)),f.update()}a&&d(q)},r=function(a,b,e,f,g,i,s){var t={id:h(),start:a,end:b,b:e,s:0,dur:f-e,spd:1,get:g,set:i,easing:s||r.linear,status:j,speed:k,duration:l,stop:m,pause:n,resume:o,update:p};c[t.id]=t;var u,v=0;for(u in c)if(c.hasOwnProperty(u)&&(v++,2==v))break;return 1==v&&d(q),t};return r.time=i,r.getById=function(a){return c[a]||null},r.linear=function(a){return a},r.easeout=function(a){return Math.pow(a,1.7)},r.easein=function(a){return Math.pow(a,.48)},r.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=.48-a/1.04,c=Math.sqrt(.1734+b*b),d=c-b,e=Math.pow(Math.abs(d),1/3)*(0>d?-1:1),f=-c-b,g=Math.pow(Math.abs(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},r.backin=function(a){if(1==a)return 1;var b=1.70158;return a*a*((b+1)*a-b)},r.backout=function(a){if(0==a)return 0;a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},r.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-.075)*Math.PI/.3)+1},r.bounce=function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b},a.mina=r,r}("undefined"==typeof b?function(){}:b),d=function(a){function c(a,b){if(a){if(a.nodeType)return w(a);if(e(a,"array")&&c.set)return c.set.apply(c,a);if(a instanceof s)return a;if(null==b)return a=y.doc.querySelector(String(a)),w(a)}return a=null==a?"100%":a,b=null==b?"100%":b,new v(a,b)}function d(a,b){if(b){if("#text"==a&&(a=y.doc.createTextNode(b.text||b["#text"]||"")),"#comment"==a&&(a=y.doc.createComment(b.text||b["#text"]||"")),"string"==typeof a&&(a=d(a)),"string"==typeof b)return 1==a.nodeType?"xlink:"==b.substring(0,6)?a.getAttributeNS(T,b.substring(6)):"xml:"==b.substring(0,4)?a.getAttributeNS(U,b.substring(4)):a.getAttribute(b):"text"==b?a.nodeValue:null;if(1==a.nodeType){for(var c in b)if(b[z](c)){var e=A(b[c]);e?"xlink:"==c.substring(0,6)?a.setAttributeNS(T,c.substring(6),e):"xml:"==c.substring(0,4)?a.setAttributeNS(U,c.substring(4),e):a.setAttribute(c,e):a.removeAttribute(c)}}else"text"in b&&(a.nodeValue=b.text)}else a=y.doc.createElementNS(U,a);return a}function e(a,b){return b=A.prototype.toLowerCase.call(b),"finite"==b?isFinite(a):"array"==b&&(a instanceof Array||Array.isArray&&Array.isArray(a))?!0:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||J.call(a).slice(8,-1).toLowerCase()==b}function f(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[z](c)&&(b[c]=f(a[c]));return b}function h(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function i(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),g=d.cache=d.cache||{},i=d.count=d.count||[];return g[z](f)?(h(i,f),c?c(g[f]):g[f]):(i.length>=1e3&&delete g[i.shift()],i.push(f),g[f]=a.apply(b,e),c?c(g[f]):g[f])}return d}function j(a,b,c,d,e,f){if(null==e){var g=a-c,h=b-d;return g||h?(180+180*D.atan2(-h,-g)/H+360)%360:0}return j(a,b,e,f)-j(c,d,e,f)}function k(a){return a%360*H/180}function l(a){return 180*a/H%360}function m(a){var b=[];return a=a.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g,function(a,c,d){return d=d.split(/\s*,\s*|\s+/),"rotate"==c&&1==d.length&&d.push(0,0),"scale"==c&&(d.length>2?d=d.slice(0,2):2==d.length&&d.push(0,0),1==d.length&&d.push(d[0],0,0)),b.push("skewX"==c?["m",1,0,D.tan(k(d[0])),1,0,0]:"skewY"==c?["m",1,D.tan(k(d[0])),0,1,0,0]:[c.charAt(0)].concat(d)),a}),b}function n(a,b){var d=ab(a),e=new c.Matrix;if(d)for(var f=0,g=d.length;g>f;f++){var h,i,j,k,l,m=d[f],n=m.length,o=A(m[0]).toLowerCase(),p=m[0]!=o,q=p?e.invert():0;"t"==o&&2==n?e.translate(m[1],0):"t"==o&&3==n?p?(h=q.x(0,0),i=q.y(0,0),j=q.x(m[1],m[2]),k=q.y(m[1],m[2]),e.translate(j-h,k-i)):e.translate(m[1],m[2]):"r"==o?2==n?(l=l||b,e.rotate(m[1],l.x+l.width/2,l.y+l.height/2)):4==n&&(p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.rotate(m[1],j,k)):e.rotate(m[1],m[2],m[3])):"s"==o?2==n||3==n?(l=l||b,e.scale(m[1],m[n-1],l.x+l.width/2,l.y+l.height/2)):4==n?p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.scale(m[1],m[1],j,k)):e.scale(m[1],m[1],m[2],m[3]):5==n&&(p?(j=q.x(m[3],m[4]),k=q.y(m[3],m[4]),e.scale(m[1],m[2],j,k)):e.scale(m[1],m[2],m[3],m[4])):"m"==o&&7==n&&e.add(m[1],m[2],m[3],m[4],m[5],m[6])}return e}function o(a){var b=a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||a.node.parentNode&&w(a.node.parentNode)||c.select("svg")||c(0,0),d=b.select("defs"),e=null==d?!1:d.node;return e||(e=u("defs",b.node).node),e}function p(a){return a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||c.select("svg")}function q(a,b,c){function e(a){if(null==a)return I;if(a==+a)return a;d(j,{width:a});try{return j.getBBox().width}catch(b){return 0}}function f(a){if(null==a)return I;if(a==+a)return a;d(j,{height:a});try{return j.getBBox().height}catch(b){return 0}}function g(d,e){null==b?i[d]=e(a.attr(d)||0):d==b&&(i=e(null==c?a.attr(d)||0:c))}var h=p(a).node,i={},j=h.querySelector(".svg---mgr");switch(j||(j=d("rect"),d(j,{x:-9e9,y:-9e9,width:10,height:10,"class":"svg---mgr",fill:"none"}),h.appendChild(j)),a.type){case"rect":g("rx",e),g("ry",f);case"image":g("width",e),g("height",f);case"text":g("x",e),g("y",f);break;case"circle":g("cx",e),g("cy",f),g("r",e);break;case"ellipse":g("cx",e),g("cy",f),g("rx",e),g("ry",f);break;case"line":g("x1",e),g("x2",e),g("y1",f),g("y2",f);break;case"marker":g("refX",e),g("markerWidth",e),g("refY",f),g("markerHeight",f);break;case"radialGradient":g("fx",e),g("fy",f);break;case"tspan":g("dx",e),g("dy",f);break;default:g(b,e)}return h.removeChild(j),i}function r(a){e(a,"array")||(a=Array.prototype.slice.call(arguments,0));for(var b=0,c=0,d=this.node;this[b];)delete this[b++];for(b=0;bc;c++){var e={type:a[c].type,attr:a[c].attr()},f=a[c].children();b.push(e),f.length&&x(f,e.childNodes=[])}}c.version="0.4.0",c.toString=function(){return"Snap v"+this.version},c._={};var y={win:a.window,doc:a.window.document};c._.glob=y;{var z="hasOwnProperty",A=String,B=parseFloat,C=parseInt,D=Math,E=D.max,F=D.min,G=D.abs,H=(D.pow,D.PI),I=(D.round,""),J=Object.prototype.toString,K=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,L=(c._.separator=/[,\s]+/,/[\s]*,[\s]*/),M={hs:1,rg:1},N=/([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,O=/([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,P=/(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/gi,Q=0,R="S"+(+new Date).toString(36),S=function(a){return(a&&a.type?a.type:I)+R+(Q++).toString(36)},T="http://www.w3.org/1999/xlink",U="http://www.w3.org/2000/svg",V={};c.url=function(a){return"url('#"+a+"')"}}c._.$=d,c._.id=S,c.format=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return A(b).replace(a,function(a,b){return c(a,b,d)})}}(),c._.clone=f,c._.cacher=i,c.rad=k,c.deg=l,c.sin=function(a){return D.sin(c.rad(a))},c.tan=function(a){return D.tan(c.rad(a))},c.cos=function(a){return D.cos(c.rad(a))},c.asin=function(a){return c.deg(D.asin(a))},c.acos=function(a){return c.deg(D.acos(a))},c.atan=function(a){return c.deg(D.atan(a))},c.atan2=function(a){return c.deg(D.atan2(a))},c.angle=j,c.len=function(a,b,d,e){return Math.sqrt(c.len2(a,b,d,e))},c.len2=function(a,b,c,d){return(a-c)*(a-c)+(b-d)*(b-d)},c.closestPoint=function(a,b,c){function d(a){var d=a.x-b,e=a.y-c;return d*d+e*e}for(var e,f,g,h,i=a.node,j=i.getTotalLength(),k=j/i.pathSegList.numberOfItems*.125,l=1/0,m=0;j>=m;m+=k)(h=d(g=i.getPointAtLength(m))).5;){var n,o,p,q,r,s;(p=f-k)>=0&&(r=d(n=i.getPointAtLength(p)))f)return b-f;if(f>a-c)return b-f+a}return b},c.getRGB=i(function(a){if(!a||(a=A(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:Z};if(!(M[z](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=W(a)),!a)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};var b,d,f,g,h,i,j=a.match(K);return j?(j[2]&&(f=C(j[2].substring(5),16),d=C(j[2].substring(3,5),16),b=C(j[2].substring(1,3),16)),j[3]&&(f=C((h=j[3].charAt(3))+h,16),d=C((h=j[3].charAt(2))+h,16),b=C((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=B(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),f=B(i[2]),"%"==i[2].slice(-1)&&(f*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsb2rgb(b,d,f,g)):j[6]?(i=j[6].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsl2rgb(b,d,f,g)):(b=F(D.round(b),255),d=F(D.round(d),255),f=F(D.round(f),255),g=F(E(g,0),1),j={r:b,g:d,b:f,toString:Z},j.hex="#"+(16777216|f|d<<8|b<<16).toString(16).slice(1),j.opacity=e(g,"finite")?g:1,j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z}},c),c.hsb=i(function(a,b,d){return c.hsb2rgb(a,b,d).hex}),c.hsl=i(function(a,b,d){return c.hsl2rgb(a,b,d).hex}),c.rgb=i(function(a,b,c,d){if(e(d,"finite")){var f=D.round;return"rgba("+[f(a),f(b),f(c),+d.toFixed(2)]+")"}return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)});var W=function(a){var b=y.doc.getElementsByTagName("head")[0]||y.doc.getElementsByTagName("svg")[0],c="rgb(255, 0, 0)";return(W=i(function(a){if("red"==a.toLowerCase())return c;b.style.color=c,b.style.color=a;var d=y.doc.defaultView.getComputedStyle(b,I).getPropertyValue("color");return d==c?null:d}))(a)},X=function(){return"hsb("+[this.h,this.s,this.b]+")"},Y=function(){return"hsl("+[this.h,this.s,this.l]+")"},Z=function(){return 1==this.opacity||null==this.opacity?this.hex:"rgba("+[this.r,this.g,this.b,this.opacity]+")"},$=function(a,b,d){if(null==b&&e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,b=a.g,a=a.r),null==b&&e(a,string)){var f=c.getRGB(a);a=f.r,b=f.g,d=f.b}return(a>1||b>1||d>1)&&(a/=255,b/=255,d/=255),[a,b,d]},_=function(a,b,d,f){a=D.round(255*a),b=D.round(255*b),d=D.round(255*d);var g={r:a,g:b,b:d,opacity:e(f,"finite")?f:1,hex:c.rgb(a,b,d),toString:Z};return e(f,"finite")&&(g.opacity=f),g};c.color=function(a){var b;return e(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=c.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):e(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=c.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):(e(a,"string")&&(a=c.getRGB(a)),e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&!("error"in a)?(b=c.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=c.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1,a.error=1)),a.toString=Z,a},c.hsb2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var f,g,h,i,j;return a=a%360/60,j=c*b,i=j*(1-G(a%2-1)),f=g=h=c-j,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.hsl2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var f,g,h,i,j;return a=a%360/60,j=2*b*(.5>c?c:1-c),i=j*(1-G(a%2-1)),f=g=h=c-j/2,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.rgb2hsb=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=E(a,b,c),g=f-F(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:X}},c.rgb2hsl=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=E(a,b,c),h=F(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:Y}},c.parsePathString=function(a){if(!a)return null;var b=c.path(a);if(b.arr)return c.path.clone(b.arr);var d={a:7,c:6,o:2,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,u:3,z:0},f=[];return e(a,"array")&&e(a[0],"array")&&(f=c.path.clone(a)),f.length||A(a).replace(N,function(a,b,c){var e=[],g=b.toLowerCase();if(c.replace(P,function(a,b){b&&e.push(+b)}),"m"==g&&e.length>2&&(f.push([b].concat(e.splice(0,2))),g="l",b="m"==b?"l":"L"),"o"==g&&1==e.length&&f.push([b,e[0]]),"r"==g)f.push([b].concat(e));else for(;e.length>=d[g]&&(f.push([b].concat(e.splice(0,d[g]))),d[g]););}),f.toString=c.path.toString,b.arr=c.path.clone(f),f};var ab=c.parseTransformString=function(a){if(!a)return null;var b=[];return e(a,"array")&&e(a[0],"array")&&(b=c.path.clone(a)),b.length||A(a).replace(O,function(a,c,d){{var e=[];c.toLowerCase()}d.replace(P,function(a,b){b&&e.push(+b)}),b.push([c].concat(e))}),b.toString=c.path.toString,b};c._.svgTransform2string=m,c._.rgTransform=/^[a-z][\s]*-?\.?\d/i,c._.transform2matrix=n,c._unit2px=q;y.doc.contains||y.doc.compareDocumentPosition?function(a,b){var c=9==a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a==d||!(!d||1!=d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b;)if(b=b.parentNode,b==a)return!0;return!1};c._.getSomeDefs=o,c._.getSomeSVG=p,c.select=function(a){return a=A(a).replace(/([^\\]):/g,"$1\\:"),w(y.doc.querySelector(a))},c.selectAll=function(a){for(var b=y.doc.querySelectorAll(a),d=(c.set||Array)(),e=0;ei;i++)h[g[i].nodeName]=g[i].nodeValue;return h}if(e(a,"string")){if(!(arguments.length>1))return b("snap.util.getattr."+a,d).firstDefined();var k={};k[a]=c,a=k}for(var l in a)a[z](l)&&b("snap.util.attr."+l,d,a[l]);return d},c.parse=function(a){var b=y.doc.createDocumentFragment(),c=!0,d=y.doc.createElement("div");if(a=A(a),a.match(/^\s*<\s*svg(?:\s|>)/)||(a=""+a+"",c=!1),d.innerHTML=a,a=d.getElementsByTagName("svg")[0])if(c)b=a;else for(;a.firstChild;)b.appendChild(a.firstChild);return new t(b)},c.fragment=function(){for(var a=Array.prototype.slice.call(arguments,0),b=y.doc.createDocumentFragment(),d=0,e=a.length;e>d;d++){var f=a[d];f.node&&f.node.nodeType&&b.appendChild(f.node),f.nodeType&&b.appendChild(f),"string"==typeof f&&b.appendChild(c.parse(f).node)}return new t(b)},c._.make=u,c._.wrap=w,v.prototype.el=function(a,b){var c=u(a,this.node);return b&&c.attr(b),c},s.prototype.children=function(){for(var a=[],b=this.node.childNodes,d=0,e=b.length;e>d;d++)a[d]=c(b[d]);return a},s.prototype.toJSON=function(){var a=[];return x([this],a),a[0]},b.on("snap.util.getattr",function(){var a=b.nt();a=a.substring(a.lastIndexOf(".")+1);var c=a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});return bb[z](c)?this.node.ownerDocument.defaultView.getComputedStyle(this.node,null).getPropertyValue(c):d(this.node,a)});var bb={"alignment-baseline":0,"baseline-shift":0,clip:0,"clip-path":0,"clip-rule":0,color:0,"color-interpolation":0,"color-interpolation-filters":0,"color-profile":0,"color-rendering":0,cursor:0,direction:0,display:0,"dominant-baseline":0,"enable-background":0,fill:0,"fill-opacity":0,"fill-rule":0,filter:0,"flood-color":0,"flood-opacity":0,font:0,"font-family":0,"font-size":0,"font-size-adjust":0,"font-stretch":0,"font-style":0,"font-variant":0,"font-weight":0,"glyph-orientation-horizontal":0,"glyph-orientation-vertical":0,"image-rendering":0,kerning:0,"letter-spacing":0,"lighting-color":0,marker:0,"marker-end":0,"marker-mid":0,"marker-start":0,mask:0,opacity:0,overflow:0,"pointer-events":0,"shape-rendering":0,"stop-color":0,"stop-opacity":0,stroke:0,"stroke-dasharray":0,"stroke-dashoffset":0,"stroke-linecap":0,"stroke-linejoin":0,"stroke-miterlimit":0,"stroke-opacity":0,"stroke-width":0,"text-anchor":0,"text-decoration":0,"text-rendering":0,"unicode-bidi":0,visibility:0,"word-spacing":0,"writing-mode":0};b.on("snap.util.attr",function(a){var c=b.nt(),e={};c=c.substring(c.lastIndexOf(".")+1),e[c]=a;var f=c.replace(/-(\w)/gi,function(a,b){return b.toUpperCase()}),g=c.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});bb[z](g)?this.node.style[f]=null==a?I:a:d(this.node,e)}),function(){}(v.prototype),c.ajax=function(a,c,d,f){var g=new XMLHttpRequest,h=S();if(g){if(e(c,"function"))f=d,d=c,c=null;else if(e(c,"object")){var i=[];for(var j in c)c.hasOwnProperty(j)&&i.push(encodeURIComponent(j)+"="+encodeURIComponent(c[j]));c=i.join("&")}return g.open(c?"POST":"GET",a,!0),c&&(g.setRequestHeader("X-Requested-With","XMLHttpRequest"),g.setRequestHeader("Content-type","application/x-www-form-urlencoded")),d&&(b.once("snap.ajax."+h+".0",d),b.once("snap.ajax."+h+".200",d),b.once("snap.ajax."+h+".304",d)),g.onreadystatechange=function(){4==g.readyState&&b("snap.ajax."+h+"."+g.status,f,g)},4==g.readyState?g:(g.send(c),g)}},c.load=function(a,b,d){c.ajax(a,function(a){var e=c.parse(a.responseText);d?b.call(d,e):b(e)})};var cb=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,h=e.clientLeft||d.clientLeft||0,i=b.top+(g.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(g.win.pageXOffset||e.scrollLeft||d.scrollLeft)-h;return{y:i,x:j}};return c.getElementByPoint=function(a,b){var c=this,d=(c.canvas,y.doc.elementFromPoint(a,b));if(y.win.opera&&"svg"==d.tagName){var e=cb(d),f=d.createSVGRect();f.x=a-e.x,f.y=b-e.y,f.width=f.height=1;var g=d.getIntersectionList(f,null);g.length&&(d=g[g.length-1])}return d?w(d):null},c.plugin=function(a){a(c,s,v,y,t)},y.win.Snap=c,c}(a||this);return d.plugin(function(d,e,f,g,h){function i(a,b){if(null==b){var c=!0;if(b=a.node.getAttribute("linearGradient"==a.type||"radialGradient"==a.type?"gradientTransform":"pattern"==a.type?"patternTransform":"transform"),!b)return new d.Matrix;b=d._.svgTransform2string(b)}else b=d._.rgTransform.test(b)?o(b).replace(/\.{3}|\u2026/g,a._.transform||""):d._.svgTransform2string(b),n(b,"array")&&(b=d.path?d.path.toString.call(b):o(b)),a._.transform=b;var e=d._.transform2matrix(b,a.getBBox(1));return c?e:void(a.matrix=e)}function j(a){function b(a,b){var c=q(a.node,b);c=c&&c.match(f),c=c&&c[2],c&&"#"==c.charAt()&&(c=c.substring(1),c&&(h[c]=(h[c]||[]).concat(function(c){var d={};d[b]=URL(c),q(a.node,d)})))}function c(a){var b=q(a.node,"xlink:href");b&&"#"==b.charAt()&&(b=b.substring(1),b&&(h[b]=(h[b]||[]).concat(function(b){a.attr("xlink:href","#"+b)})))}for(var d,e=a.selectAll("*"),f=/^\s*url\(("|'|)(.*)\1\)\s*$/,g=[],h={},i=0,j=e.length;j>i;i++){d=e[i],b(d,"fill"),b(d,"stroke"),b(d,"filter"),b(d,"mask"),b(d,"clip-path"),c(d);var k=q(d.node,"id");k&&(q(d.node,{id:d.id}),g.push({old:k,id:d.id}))}for(i=0,j=g.length;j>i;i++){var l=h[g[i].old];if(l)for(var m=0,n=l.length;n>m;m++)l[m](g[i].id)}}function k(a,b,c){return function(d){var e=d.slice(a,b);return 1==e.length&&(e=e[0]),c?c(e):e}}function l(a){return function(){var b=a?"<"+this.type:"",c=this.node.attributes,d=this.node.childNodes;if(a)for(var e=0,f=c.length;f>e;e++)b+=" "+c[e].name+'="'+c[e].value.replace(/"/g,'\\"')+'"';if(d.length){for(a&&(b+=">"),e=0,f=d.length;f>e;e++)3==d[e].nodeType?b+=d[e].nodeValue:1==d[e].nodeType&&(b+=u(d[e]).toString());a&&(b+="")}else a&&(b+="/>");return b}}var m=e.prototype,n=d.is,o=String,p=d._unit2px,q=d._.$,r=d._.make,s=d._.getSomeDefs,t="hasOwnProperty",u=d._.wrap;m.getBBox=function(a){if(!d.Matrix||!d.path)return this.node.getBBox();var b=this,c=new d.Matrix;if(b.removed)return d._.box();for(;"use"==b.type;)if(a||(c=c.add(b.transform().localMatrix.translate(b.attr("x")||0,b.attr("y")||0))),b.original)b=b.original;else{var e=b.attr("xlink:href");b=b.original=b.node.ownerDocument.getElementById(e.substring(e.indexOf("#")+1))}var f=b._,g=d.path.get[b.type]||d.path.get.deflt;try{return a?(f.bboxwt=g?d.path.getBBox(b.realPath=g(b)):d._.box(b.node.getBBox()),d._.box(f.bboxwt)):(b.realPath=g(b),b.matrix=b.transform().localMatrix,f.bbox=d.path.getBBox(d.path.map(b.realPath,c.add(b.matrix))),d._.box(f.bbox))}catch(h){return d._.box()}};var v=function(){return this.string};m.transform=function(a){var b=this._;if(null==a){for(var c,e=this,f=new d.Matrix(this.node.getCTM()),g=i(this),h=[g],j=new d.Matrix,k=g.toTransformString(),l=o(g)==o(this.matrix)?o(b.transform):k;"svg"!=e.type&&(e=e.parent());)h.push(i(e));for(c=h.length;c--;)j.add(h[c]);return{string:l,globalMatrix:f,totalMatrix:j,localMatrix:g,diffMatrix:f.clone().add(g.invert()),global:f.toTransformString(),total:j.toTransformString(),local:k,toString:v}}return a instanceof d.Matrix?(this.matrix=a,this._.transform=a.toTransformString()):i(this,a),this.node&&("linearGradient"==this.type||"radialGradient"==this.type?q(this.node,{gradientTransform:this.matrix}):"pattern"==this.type?q(this.node,{patternTransform:this.matrix}):q(this.node,{transform:this.matrix})),this},m.parent=function(){return u(this.node.parentNode)},m.append=m.add=function(a){if(a){if("set"==a.type){var b=this;return a.forEach(function(a){b.add(a)}),this}a=u(a),this.node.appendChild(a.node),a.paper=this.paper}return this},m.appendTo=function(a){return a&&(a=u(a),a.append(this)),this},m.prepend=function(a){if(a){if("set"==a.type){var b,c=this;return a.forEach(function(a){b?b.after(a):c.prepend(a),b=a}),this}a=u(a);var d=a.parent();this.node.insertBefore(a.node,this.node.firstChild),this.add&&this.add(),a.paper=this.paper,this.parent()&&this.parent().add(),d&&d.add()}return this},m.prependTo=function(a){return a=u(a),a.prepend(this),this},m.before=function(a){if("set"==a.type){var b=this;return a.forEach(function(a){var c=a.parent();b.node.parentNode.insertBefore(a.node,b.node),c&&c.add()}),this.parent().add(),this}a=u(a);var c=a.parent();return this.node.parentNode.insertBefore(a.node,this.node),this.parent()&&this.parent().add(),c&&c.add(),a.paper=this.paper,this},m.after=function(a){a=u(a);var b=a.parent();return this.node.nextSibling?this.node.parentNode.insertBefore(a.node,this.node.nextSibling):this.node.parentNode.appendChild(a.node),this.parent()&&this.parent().add(),b&&b.add(),a.paper=this.paper,this},m.insertBefore=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.insertAfter=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node.nextSibling),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.remove=function(){var a=this.parent();return this.node.parentNode&&this.node.parentNode.removeChild(this.node),delete this.paper,this.removed=!0,a&&a.add(),this},m.select=function(a){return u(this.node.querySelector(a))},m.selectAll=function(a){for(var b=this.node.querySelectorAll(a),c=(d.set||Array)(),e=0;eb;b++)a[b].stop();return this},m.animate=function(a,d,e,f){"function"!=typeof e||e.length||(f=e,e=c.linear),a instanceof w&&(f=a.callback,e=a.easing,d=a.dur,a=a.attr);var g,h,i,j,l=[],m=[],p={},q=this;for(var r in a)if(a[t](r)){q.equal?(j=q.equal(r,o(a[r])),g=j.from,h=j.to,i=j.f):(g=+q.attr(r),h=+a[r]);var s=n(g,"array")?g.length:1;p[r]=k(l.length,l.length+s,i),l=l.concat(g),m=m.concat(h)}var u=c.time(),v=c(l,m,u,u+d,c.time,function(a){var b={};for(var c in p)p[t](c)&&(b[c]=p[c](a));q.attr(b)},e);return q.anims[v.id]=v,v._attrs=a,v._callback=f,b("snap.animcreated."+q.id,v),b.once("mina.finish."+v.id,function(){delete q.anims[v.id],f&&f.call(q)}),b.once("mina.stop."+v.id,function(){delete q.anims[v.id]}),q};var x={};m.data=function(a,c){var e=x[this.id]=x[this.id]||{};if(0==arguments.length)return b("snap.data.get."+this.id,this,e,null),e; -if(1==arguments.length){if(d.is(a,"object")){for(var f in a)a[t](f)&&this.data(f,a[f]);return this}return b("snap.data.get."+this.id,this,e[a],a),e[a]}return e[a]=c,b("snap.data.set."+this.id,this,c,a),this},m.removeData=function(a){return null==a?x[this.id]={}:x[this.id]&&delete x[this.id][a],this},m.outerSVG=m.toString=l(1),m.innerSVG=l(),m.toDataURL=function(){if(a&&a.btoa){var b=this.getBBox(),c=d.format('{contents}',{x:+b.x.toFixed(3),y:+b.y.toFixed(3),width:+b.width.toFixed(3),height:+b.height.toFixed(3),contents:this.outerSVG()});return"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(c)))}},h.prototype.select=m.select,h.prototype.selectAll=m.selectAll}),d.plugin(function(a){function b(a,b,d,e,f,g){return null==b&&"[object SVGMatrix]"==c.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,void(this.f=a.f)):void(null!=a?(this.a=+a,this.b=+b,this.c=+d,this.d=+e,this.e=+f,this.f=+g):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0))}var c=Object.prototype.toString,d=String,e=Math,f="";!function(c){function g(a){return a[0]*a[0]+a[1]*a[1]}function h(a){var b=e.sqrt(g(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}c.add=function(a,c,d,e,f,g){var h,i,j,k,l=[[],[],[]],m=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],n=[[a,d,f],[c,e,g],[0,0,1]];for(a&&a instanceof b&&(n=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),h=0;3>h;h++)for(i=0;3>i;i++){for(k=0,j=0;3>j;j++)k+=m[h][j]*n[j][i];l[h][i]=k}return this.a=l[0][0],this.b=l[1][0],this.c=l[0][1],this.d=l[1][1],this.e=l[0][2],this.f=l[1][2],this},c.invert=function(){var a=this,c=a.a*a.d-a.b*a.c;return new b(a.d/c,-a.b/c,-a.c/c,a.a/c,(a.c*a.f-a.d*a.e)/c,(a.b*a.e-a.a*a.f)/c)},c.clone=function(){return new b(this.a,this.b,this.c,this.d,this.e,this.f)},c.translate=function(a,b){return this.add(1,0,0,1,a,b)},c.scale=function(a,b,c,d){return null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d),this},c.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var f=+e.cos(b).toFixed(9),g=+e.sin(b).toFixed(9);return this.add(f,g,-g,f,c,d),this.add(1,0,0,1,-c,-d)},c.x=function(a,b){return a*this.a+b*this.c+this.e},c.y=function(a,b){return a*this.b+b*this.d+this.f},c.get=function(a){return+this[d.fromCharCode(97+a)].toFixed(4)},c.toString=function(){return"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")"},c.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},c.determinant=function(){return this.a*this.d-this.b*this.c},c.split=function(){var b={};b.dx=this.e,b.dy=this.f;var c=[[this.a,this.c],[this.b,this.d]];b.scalex=e.sqrt(g(c[0])),h(c[0]),b.shear=c[0][0]*c[1][0]+c[0][1]*c[1][1],c[1]=[c[1][0]-c[0][0]*b.shear,c[1][1]-c[0][1]*b.shear],b.scaley=e.sqrt(g(c[1])),h(c[1]),b.shear/=b.scaley,this.determinant()<0&&(b.scalex=-b.scalex);var d=-c[0][1],f=c[1][1];return 0>f?(b.rotate=a.deg(e.acos(f)),0>d&&(b.rotate=360-b.rotate)):b.rotate=a.deg(e.asin(d)),b.isSimple=!(+b.shear.toFixed(9)||b.scalex.toFixed(9)!=b.scaley.toFixed(9)&&b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate,b},c.toTransformString=function(a){var b=a||this.split();return+b.shear.toFixed(9)?"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]:(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[+b.dx.toFixed(4),+b.dy.toFixed(4)]:f)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:f)+(b.rotate?"r"+[+b.rotate.toFixed(4),0,0]:f))}}(b.prototype),a.Matrix=b,a.matrix=function(a,c,d,e,f,g){return new b(a,c,d,e,f,g)}}),d.plugin(function(a,c,d,e,f){function g(d){return function(e){if(b.stop(),e instanceof f&&1==e.node.childNodes.length&&("radialGradient"==e.node.firstChild.tagName||"linearGradient"==e.node.firstChild.tagName||"pattern"==e.node.firstChild.tagName)&&(e=e.node.firstChild,n(this).appendChild(e),e=l(e)),e instanceof c)if("radialGradient"==e.type||"linearGradient"==e.type||"pattern"==e.type){e.node.id||p(e.node,{id:e.id});var g=q(e.node.id)}else g=e.attr(d);else if(g=a.color(e),g.error){var h=a(n(this).ownerSVGElement).gradient(e);h?(h.node.id||p(h.node,{id:h.id}),g=q(h.node.id)):g=e}else g=r(g);var i={};i[d]=g,p(this.node,i),this.node.style[d]=t}}function h(a){b.stop(),a==+a&&(a+="px"),this.node.style.fontSize=a}function i(a){for(var b=[],c=a.childNodes,d=0,e=c.length;e>d;d++){var f=c[d];3==f.nodeType&&b.push(f.nodeValue),"tspan"==f.tagName&&b.push(1==f.childNodes.length&&3==f.firstChild.nodeType?f.firstChild.nodeValue:i(f))}return b}function j(){return b.stop(),this.node.style.fontSize}var k=a._.make,l=a._.wrap,m=a.is,n=a._.getSomeDefs,o=/^url\(#?([^)]+)\)$/,p=a._.$,q=a.url,r=String,s=a._.separator,t="";b.on("snap.util.attr.mask",function(a){if(a instanceof c||a instanceof f){if(b.stop(),a instanceof f&&1==a.node.childNodes.length&&(a=a.node.firstChild,n(this).appendChild(a),a=l(a)),"mask"==a.type)var d=a;else d=k("mask",n(this)),d.node.appendChild(a.node);!d.node.id&&p(d.node,{id:d.id}),p(this.node,{mask:q(d.id)})}}),function(a){b.on("snap.util.attr.clip",a),b.on("snap.util.attr.clip-path",a),b.on("snap.util.attr.clipPath",a)}(function(a){if(a instanceof c||a instanceof f){if(b.stop(),"clipPath"==a.type)var d=a;else d=k("clipPath",n(this)),d.node.appendChild(a.node),!d.node.id&&p(d.node,{id:d.id});p(this.node,{"clip-path":q(d.node.id||d.id)})}}),b.on("snap.util.attr.fill",g("fill")),b.on("snap.util.attr.stroke",g("stroke"));var u=/^([lr])(?:\(([^)]*)\))?(.*)$/i;b.on("snap.util.grad.parse",function(a){a=r(a);var b=a.match(u);if(!b)return null;var c=b[1],d=b[2],e=b[3];return d=d.split(/\s*,\s*/).map(function(a){return+a==a?+a:a}),1==d.length&&0==d[0]&&(d=[]),e=e.split("-"),e=e.map(function(a){a=a.split(":");var b={color:a[0]};return a[1]&&(b.offset=parseFloat(a[1])),b}),{type:c,params:d,stops:e}}),b.on("snap.util.attr.d",function(c){b.stop(),m(c,"array")&&m(c[0],"array")&&(c=a.path.toString.call(c)),c=r(c),c.match(/[ruo]/i)&&(c=a.path.toAbsolute(c)),p(this.node,{d:c})})(-1),b.on("snap.util.attr.#text",function(a){b.stop(),a=r(a);for(var c=e.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(c)})(-1),b.on("snap.util.attr.path",function(a){b.stop(),this.attr({d:a})})(-1),b.on("snap.util.attr.class",function(a){b.stop(),this.node.className.baseVal=a})(-1),b.on("snap.util.attr.viewBox",function(a){var c;c=m(a,"object")&&"x"in a?[a.x,a.y,a.width,a.height].join(" "):m(a,"array")?a.join(" "):a,p(this.node,{viewBox:c}),b.stop()})(-1),b.on("snap.util.attr.transform",function(a){this.transform(a),b.stop()})(-1),b.on("snap.util.attr.r",function(a){"rect"==this.type&&(b.stop(),p(this.node,{rx:a,ry:a}))})(-1),b.on("snap.util.attr.textpath",function(a){if(b.stop(),"text"==this.type){var d,e,f;if(!a&&this.textPath){for(e=this.textPath;e.node.firstChild;)this.node.appendChild(e.node.firstChild);return e.remove(),void delete this.textPath}if(m(a,"string")){var g=n(this),h=l(g.parentNode).path(a);g.appendChild(h.node),d=h.id,h.attr({id:d})}else a=l(a),a instanceof c&&(d=a.attr("id"),d||(d=a.id,a.attr({id:d})));if(d)if(e=this.textPath,f=this.node,e)e.attr({"xlink:href":"#"+d});else{for(e=p("textPath",{"xlink:href":"#"+d});f.firstChild;)e.appendChild(f.firstChild);f.appendChild(e),this.textPath=l(e)}}})(-1),b.on("snap.util.attr.text",function(a){if("text"==this.type){for(var c=this.node,d=function(a){var b=p("tspan");if(m(a,"array"))for(var c=0;c1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polyline",b)},g.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polygon",b)},function(){function d(){return this.selectAll("stop")}function e(a,b){var d=k("stop"),e={offset:+b+"%"};return a=c.color(a),e["stop-color"]=a.hex,a.opacity<1&&(e["stop-opacity"]=a.opacity),k(d,e),this.node.appendChild(d),this}function f(){if("linearGradient"==this.type){var a=k(this.node,"x1")||0,b=k(this.node,"x2")||1,d=k(this.node,"y1")||0,e=k(this.node,"y2")||0;return c._.box(a,d,math.abs(b-a),math.abs(e-d))}var f=this.node.cx||.5,g=this.node.cy||.5,h=this.node.r||0;return c._.box(f-h,g-h,2*h,2*h)}function h(a,c){function d(a,b){for(var c=(b-l)/(a-m),d=m;a>d;d++)g[d].offset=+(+l+c*(d-m)).toFixed(2);m=a,l=b}var e,f=b("snap.util.grad.parse",null,c).firstDefined();if(!f)return null;f.params.unshift(a),e="l"==f.type.toLowerCase()?i.apply(0,f.params):j.apply(0,f.params),f.type!=f.type.toLowerCase()&&k(e.node,{gradientUnits:"userSpaceOnUse"});var g=f.stops,h=g.length,l=0,m=0;h--;for(var n=0;h>n;n++)"offset"in g[n]&&d(n,g[n].offset);for(g[h].offset=g[h].offset||100,d(h,g[h].offset),n=0;h>=n;n++){var o=g[n];e.addStop(o.color,o.offset)}return e}function i(a,b,g,h,i){var j=c._.make("linearGradient",a);return j.stops=d,j.addStop=e,j.getBBox=f,null!=b&&k(j.node,{x1:b,y1:g,x2:h,y2:i}),j}function j(a,b,g,h,i,j){var l=c._.make("radialGradient",a);return l.stops=d,l.addStop=e,l.getBBox=f,null!=b&&k(l.node,{cx:b,cy:g,r:h}),null!=i&&null!=j&&k(l.node,{fx:i,fy:j}),l}var k=c._.$;g.gradient=function(a){return h(this.defs,a)},g.gradientLinear=function(a,b,c,d){return i(this.defs,a,b,c,d)},g.gradientRadial=function(a,b,c,d,e){return j(this.defs,a,b,c,d,e)},g.toString=function(){var a,b=this.node.ownerDocument,d=b.createDocumentFragment(),e=b.createElement("div"),f=this.node.cloneNode(!0);return d.appendChild(e),e.appendChild(f),c._.$(f,{xmlns:"http://www.w3.org/2000/svg"}),a=e.innerHTML,d.removeChild(d.firstChild),a},g.toDataURL=function(){return a&&a.btoa?"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(this))):void 0},g.clear=function(){for(var a,b=this.node.firstChild;b;)a=b.nextSibling,"defs"!=b.tagName?b.parentNode.removeChild(b):g.clear.call({node:b}),b=a}}()}),d.plugin(function(a,b){function c(a){var b=c.ps=c.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[K](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]}function d(a,b,c,d){return null==a&&(a=b=c=d=0),null==b&&(b=a.y,c=a.width,d=a.height,a=a.x),{x:a,y:b,width:c,w:c,height:d,h:d,x2:a+c,y2:b+d,cx:a+c/2,cy:b+d/2,r1:N.min(c,d)/2,r2:N.max(c,d)/2,r0:N.sqrt(c*c+d*d)/2,path:w(a,b,c,d),vb:[a,b,c,d].join(" ")}}function e(){return this.join(",").replace(L,"$1")}function f(a){var b=J(a);return b.toString=e,b}function g(a,b,c,d,e,f,g,h,j){return null==j?n(a,b,c,d,e,f,g,h):i(a,b,c,d,e,f,g,h,o(a,b,c,d,e,f,g,h,j))}function h(c,d){function e(a){return+(+a).toFixed(3)}return a._.cacher(function(a,f,h){a instanceof b&&(a=a.attr("d")),a=E(a);for(var j,k,l,m,n,o="",p={},q=0,r=0,s=a.length;s>r;r++){if(l=a[r],"M"==l[0])j=+l[1],k=+l[2];else{if(m=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6]),q+m>f){if(d&&!p.start){if(n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q),o+=["C"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)],h)return o;p.start=o,o=["M"+e(n.x),e(n.y)+"C"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(l[5]),e(l[6])].join(),q+=m,j=+l[5],k=+l[6];continue}if(!c&&!d)return n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q)}q+=m,j=+l[5],k=+l[6]}o+=l.shift()+l}return p.end=o,n=c?q:d?p:i(j,k,l[0],l[1],l[2],l[3],l[4],l[5],1)},null,a._.clone)}function i(a,b,c,d,e,f,g,h,i){var j=1-i,k=R(j,3),l=R(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*N.atan2(q-s,r-t)/O;return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function j(b,c,e,f,g,h,i,j){a.is(b,"array")||(b=[b,c,e,f,g,h,i,j]);var k=D.apply(null,b);return d(k.min.x,k.min.y,k.max.x-k.min.x,k.max.y-k.min.y)}function k(a,b,c){return b>=a.x&&b<=a.x+a.width&&c>=a.y&&c<=a.y+a.height}function l(a,b){return a=d(a),b=d(b),k(b,a.x,a.y)||k(b,a.x2,a.y)||k(b,a.x,a.y2)||k(b,a.x2,a.y2)||k(a,b.x,b.y)||k(a,b.x2,b.y)||k(a,b.x,b.y2)||k(a,b.x2,b.y2)||(a.xb.x||b.xa.x)&&(a.yb.y||b.ya.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(Q(a,c)Q(e,g)||Q(b,d)Q(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+Q(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+Q(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+Q(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+Q(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/8),k=~~(g/8),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=S(w.x-v.x)<.001?"y":"x",A=S(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+S((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+S((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=E(a),b=E(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return J(b.bbox);if(!a)return d();a=E(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=D(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=P.apply(0,h),n=P.apply(0,i),o=Q.apply(0,h),p=Q.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=J(q),q}function w(a,b,c,d,f){if(f)return[["M",+a+ +f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),a=+a,b=+b,c=+c,d=+d,null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(I(b,"array")&&I(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+o[6]+i,n[7]=+o[7]+j;break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(G(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(G(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=+k,j=+l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*O/180,n=O/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(O/180*f),N.sin(O/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=N.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*N.sqrt(S((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=N.asin(((c-x)/e).toFixed(9)),z=N.asin(((j-x)/e).toFixed(9));y=w>b?O-y:y,z=w>i?O-z:z,0>y&&(y=2*O+y),0>z&&(z=2*O+z),h&&y>z&&(y-=2*O),!h&&z>y&&(z-=2*O)}var A=z-y;if(S(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*N.cos(z),j=x+e*N.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),J=N.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],P=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(P[0]=2*M[0]-P[0],P[1]=2*M[1]-P[1],k)return[P,Q,R].concat(o);o=[P,Q,R].concat(o).join().split(",");for(var T=[],U=0,V=o.length;V>U;U++)T[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return T}function D(a,b,c,d,e,f,g,h){for(var i,j,k,l,m,n,o,p,q=[],r=[[],[]],s=0;2>s;++s)if(0==s?(j=6*a-12*c+6*e,i=-3*a+9*c-9*e+3*g,k=3*c-3*a):(j=6*b-12*d+6*f,i=-3*b+9*d-9*f+3*h,k=3*d-3*b),S(i)<1e-12){if(S(j)<1e-12)continue;l=-k/j,l>0&&1>l&&q.push(l)}else o=j*j-4*k*i,p=N.sqrt(o),0>o||(m=(-j+p)/(2*i),m>0&&1>m&&q.push(m),n=(-j-p)/(2*i),n>0&&1>n&&q.push(n));for(var t,u=q.length,v=u;u--;)l=q[u],t=1-l,r[0][u]=t*t*t*a+3*t*t*l*c+3*t*l*l*e+l*l*l*g,r[1][u]=t*t*t*b+3*t*t*l*d+3*t*l*l*f+l*l*l*h;return r[0][v]=a,r[1][v]=b,r[0][v+1]=g,r[1][v+1]=h,r[0].length=r[1].length=v+2,{min:{x:P.apply(0,r[0]),y:P.apply(0,r[1])},max:{x:Q.apply(0,r[0]),y:Q.apply(0,r[1])}}}function E(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b,c){var d,e;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)m[b]="A",g&&(n[b]="A"),a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),r=Q(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],r=Q(e.length,g&&g.length||0))},m=[],n=[],o="",p="",q=0,r=Q(e.length,g&&g.length||0);r>q;q++){e[q]&&(o=e[q][0]),"C"!=o&&(m[q]=o,q&&(p=m[q-1])),e[q]=j(e[q],h,p),"A"!=m[q]&&"C"==o&&(m[q]="C"),k(e,q),g&&(g[q]&&(o=g[q][0]),"C"!=o&&(n[q]=o,q&&(p=n[q-1])),g[q]=j(g[q],i,p),"A"!=n[q]&&"C"==o&&(n[q]="C"),k(g,q)),l(e,g,h,i,q),l(g,e,i,h,q);var s=e[q],t=g&&g[q],u=s.length,v=g&&t.length;h.x=s[u-2],h.y=s[u-1],h.bx=M(s[u-4])||h.x,h.by=M(s[u-3])||h.y,i.bx=g&&(M(t[v-4])||i.x),i.by=g&&(M(t[v-3])||i.y),i.x=g&&t[v-2],i.y=g&&t[v-1]}return g||(d.curve=f(e)),g?[e,g]:e}function F(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=E(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function G(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var H=b.prototype,I=a.is,J=a._.clone,K="hasOwnProperty",L=/,?([a-z]),?/gi,M=parseFloat,N=Math,O=N.PI,P=N.min,Q=N.max,R=N.pow,S=N.abs,T=h(1),U=h(),V=h(0,1),W=a._unit2px,X={path:function(a){return a.attr("path")},circle:function(a){var b=W(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=W(a);return x(b.cx||0,b.cy||0,b.rx,b.ry)},rect:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height,b.rx,b.ry)},image:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height)},line:function(a){return"M"+[a.attr("x1")||0,a.attr("y1")||0,a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=T,a.path.getPointAtLength=U,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return V(a,b).end;var d=V(a,c,1);return b?V(d,b).end:d},H.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},H.getPointAtLength=function(a){return U(this.attr("d"),a)},H.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.closest=function(b,c,e,f){for(var g=100,h=d(b-g/2,c-g/2,g,g),i=[],j=e[0].hasOwnProperty("x")?function(a){return{x:e[a].x,y:e[a].y}}:function(a){return{x:e[a],y:f[a]}},l=0;1e6>=g&&!l;){for(var m=0,n=e.length;n>m;m++){var o=j(m);if(k(h,o.x,o.y)){l++,i.push(o);break}}l||(g*=2,h=d(b-g/2,c-g/2,g,g))}if(1e6!=g){var p,q=1/0;for(m=0,n=i.length;n>m;m++){var r=a.len(b,c,i[m].x,i[m].y);q>r&&(q=r,i[m].len=r,p=i[m])}return p}},a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=X,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=E,a.path.map=F,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var d=Math.max,e=Math.min,f=function(a){if(this.items=[],this.bindings={},this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},g=f.prototype;g.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},g.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},g.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},g.animate=function(d,e,f,g){"function"!=typeof f||f.length||(g=f,f=c.linear),d instanceof a._.Animation&&(g=d.callback,f=d.easing,e=f.dur,d=d.attr);var h=arguments;if(a.is(d,"array")&&a.is(h[h.length-1],"array"))var i=!0;var j,k=function(){j?this.b=j:j=this.b},l=0,m=this,n=g&&function(){++l==m.length&&g.call(this) -};return this.forEach(function(a,c){b.once("snap.animcreated."+a.id,k),i?h[c]&&a.animate.apply(a,h[c]):a.animate(d,e,f,n)})},g.remove=function(){for(;this.length;)this.pop().remove();return this},g.bind=function(a,b,c){var d={};if("function"==typeof b)this.bindings[a]=b;else{var e=c||a;this.bindings[a]=function(a){d[e]=a,b.attr(d)}}return this},g.attr=function(a){var b={};for(var c in a)this.bindings[c]?this.bindings[c](a[c]):b[c]=a[c];for(var d=0,e=this.items.length;e>d;d++)this.items[d].attr(b);return this},g.clear=function(){for(;this.length;)this.pop()},g.splice=function(a,b){a=0>a?d(this.length+a,0):a,b=d(0,e(this.length-a,b));var c,g=[],h=[],i=[];for(c=2;cc;c++)h.push(this[a+c]);for(;cc?i[c]:g[c-j];for(c=this.items.length=this.length-=b-j;this[c];)delete this[c++];return new f(h)},g.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},g.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},g.getBBox=function(){for(var a=[],b=[],c=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),b.push(h.y),c.push(h.x+h.width),f.push(h.y+h.height)}return a=e.apply(0,a),b=e.apply(0,b),c=d.apply(0,c),f=d.apply(0,f),{x:a,y:b,x2:c,y2:f,width:c-a,height:f-b,cx:a+(c-a)/2,cy:b+(f-b)/2}},g.clone=function(a){a=new f;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},g.toString=function(){return"Snap‘s set"},g.type="set",a.Set=f,a.set=function(){var a=new f;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,c){function d(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function e(b,c,e){c=p(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];for(var f,g,h,i,l=Math.max(b.length,c.length),m=[],n=[],o=0;l>o;o++){if(h=b[o]||d(c[o]),i=c[o]||d(h),h[0]!=i[0]||"r"==h[0].toLowerCase()&&(h[2]!=i[2]||h[3]!=i[3])||"s"==h[0].toLowerCase()&&(h[3]!=i[3]||h[4]!=i[4])){b=a._.transform2matrix(b,e()),c=a._.transform2matrix(c,e()),m=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],n=[["m",c.a,c.b,c.c,c.d,c.e,c.f]];break}for(m[o]=[],n[o]=[],f=0,g=Math.max(h.length,i.length);g>f;f++)f in h&&(m[o][f]=h[f]),f in i&&(n[o][f]=i[f])}return{from:k(m),to:k(n),f:j(m)}}function f(a){return a}function g(a){return function(b){return+b.toFixed(3)+a}}function h(a){return a.join(" ")}function i(b){return a.rgb(b[0],b[1],b[2])}function j(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function k(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}function l(a){return isFinite(parseFloat(a))}function m(b,c){return a.is(b,"array")&&a.is(c,"array")?b.toString()==c.toString():!1}var n={},o=/[a-z]+$/i,p=String;n.stroke=n.fill="colour",c.prototype.equal=function(a,c){return b("snap.util.equal",this,a,c).firstDefined()},b.on("snap.util.equal",function(b,c){var d,q,r=p(this.attr(b)||""),s=this;if(l(r)&&l(c))return{from:parseFloat(r),to:parseFloat(c),f:f};if("colour"==n[b])return d=a.color(r),q=a.color(c),{from:[d.r,d.g,d.b,d.opacity],to:[q.r,q.g,q.b,q.opacity],f:i};if("viewBox"==b)return d=this.attr(b).vb.split(" ").map(Number),q=c.split(" ").map(Number),{from:d,to:q,f:h};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),e(r,c,function(){return s.getBBox(1)});if("d"==b||"path"==b)return d=a.path.toCubic(r,c),{from:k(d[0]),to:k(d[1]),f:j(d[0])};if("points"==b)return d=p(r).split(a._.separator),q=p(c).split(a._.separator),{from:d,to:q,f:function(a){return a}};var t=r.match(o),u=p(c).match(o);return t&&m(t,u)?{from:parseFloat(r),to:parseFloat(c),f:g(t)}:{from:this.asPX(b),to:this.asPX(b,c),f:f}})}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=(function(a,b){var c="y"==a?"scrollTop":"scrollLeft",d=b&&b.node?b.node.ownerDocument:e.doc;return d[c in d.documentElement?"documentElement":"body"][c]}),l=function(){return this.originalEvent.preventDefault()},m=function(){return this.originalEvent.stopPropagation()},n=function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y",d),i=k("x",d);if(h&&j[g](b))for(var n=0,o=e.targetTouches&&e.targetTouches.length;o>n;n++)if(e.targetTouches[n].target==a||a.contains(e.targetTouches[n].target)){var p=e;e=e.targetTouches[n],e.originalEvent=p,e.preventDefault=l,e.stopPropagation=m;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}},o=[],p=function(a){for(var c,d=a.clientX,e=a.clientY,f=k("y"),g=k("x"),i=o.length;i--;){if(c=o[i],h){for(var j,l=a.touches&&a.touches.length;l--;)if(j=a.touches[l],j.identifier==c.el._drag.id||c.el.node.contains(j.target)){d=j.clientX,e=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();{var m=c.el.node;m.nextSibling,m.parentNode,m.style.display}d+=g,e+=f,b("snap.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},q=function(c){a.unmousemove(p).unmouseup(q);for(var d,e=o.length;e--;)d=o[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c),b.off("snap.drag.*."+d.el.id);o=[]},r=i.length;r--;)!function(b){a[b]=f[b]=function(c,d){if(a.is(c,"function"))this.events=this.events||[],this.events.push({name:b,f:c,unbind:n(this.node||document,b,c,d||this)});else for(var e=0,f=this.events.length;f>e;e++)if(this.events[e].name==b)try{this.events[e].f.call(this)}catch(g){}return this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[r]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var s=[];f.drag=function(c,d,e,f,g,h){function i(i,j,l){(i.originalEvent||i).preventDefault(),k._drag.x=j,k._drag.y=l,k._drag.id=i.identifier,!o.length&&a.mousemove(p).mouseup(q),o.push({el:k,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+k.id,d),c&&b.on("snap.drag.move."+k.id,c),e&&b.on("snap.drag.end."+k.id,e),b("snap.drag.start."+k.id,g||f||k,j,l,i)}function j(a,c,d){b("snap.draginit."+k.id,k,a,c,d)}var k=this;if(!arguments.length){var l;return k.drag(function(a,b){this.attr({transform:l+(l?"T":"t")+[a,b]})},function(){l=this.transform().local})}return b.on("snap.draginit."+k.id,i),k._drag={},s.push({el:k,start:i,init:j}),k.mousedown(j),k},f.undrag=function(){for(var c=s.length;c--;)s[c].el==this&&(this.unmousedown(s[c].init),s.splice(c,1),b.unbind("snap.drag.*."+this.id),b.unbind("snap.draginit."+this.id));return!s.length&&a.unmousemove(p).unmouseup(q),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format('',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e,f){return"string"==typeof d&&(e=d,f=e,d=4),"string"!=typeof e&&(f=e,e="#000"),e=e||"#000",null==d&&(d=4),null==f&&(f=1),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format('',{color:e,dx:b,dy:c,blur:d,opacity:f})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format('',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)})},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format('',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format('',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format('',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format('',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d.plugin(function(a,b){var c=a._.box,d=a.is,e=/^[^a-z]*([tbmlrc])/i,f=function(){return"T"+this.dx+","+this.dy};b.prototype.getAlign=function(a,b){null==b&&d(a,"string")&&(b=a,a=null),a=a||this.paper;var g=a.getBBox?a.getBBox():c(a),h=this.getBBox(),i={};switch(b=b&&b.match(e),b=b?b[1].toLowerCase():"c"){case"t":i.dx=0,i.dy=g.y-h.y;break;case"b":i.dx=0,i.dy=g.y2-h.y2;break;case"m":i.dx=0,i.dy=g.cy-h.cy;break;case"l":i.dx=g.x-h.x,i.dy=0;break;case"r":i.dx=g.x2-h.x2,i.dy=0;break;default:i.dx=g.cx-h.cx,i.dy=0}return i.toString=f,i},b.prototype.align=function(a,b){return this.transform("..."+this.getAlign(a,b))}}),d}); diff --git a/web/pgadmin/misc/static/explain/vendor/snap.svg/snap.svg.js b/web/pgadmin/misc/static/explain/vendor/snap.svg/snap.svg.js deleted file mode 100644 index ef0fb6d4..00000000 --- a/web/pgadmin/misc/static/explain/vendor/snap.svg/snap.svg.js +++ /dev/null @@ -1,8149 +0,0 @@ -// Snap.svg 0.4.1 -// Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// build: 2015-04-13 -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ┌────────────────────────────────────────────────────────────┐ \\ -// │ Eve 0.4.2 - JavaScript Events Library │ \\ -// ├────────────────────────────────────────────────────────────┤ \\ -// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\ -// └────────────────────────────────────────────────────────────┘ \\ -(function (glob) { - var version = "0.4.2", - has = "hasOwnProperty", - separator = /[\.\/]/, - comaseparator = /\s*,\s*/, - wildcard = "*", - fun = function () {}, - numsort = function (a, b) { - return a - b; - }, - current_event, - stop, - events = {n: {}}, - firstDefined = function () { - for (var i = 0, ii = this.length; i < ii; i++) { - if (typeof this[i] != "undefined") { - return this[i]; - } - } - }, - lastDefined = function () { - var i = this.length; - while (--i) { - if (typeof this[i] != "undefined") { - return this[i]; - } - } - }, - /*\ - * eve - [ method ] - * Fires event with given `name`, given scope and other parameters. - > Arguments - - name (string) name of the *event*, dot (`.`) or slash (`/`) separated - - scope (object) context for the event handlers - - varargs (...) the rest of arguments will be sent to event handlers - = (object) array of returned values from the listeners. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value. - \*/ - eve = function (name, scope) { - name = String(name); - var e = events, - oldstop = stop, - args = Array.prototype.slice.call(arguments, 2), - listeners = eve.listeners(name), - z = 0, - f = false, - l, - indexed = [], - queue = {}, - out = [], - ce = current_event, - errors = []; - out.firstDefined = firstDefined; - out.lastDefined = lastDefined; - current_event = name; - stop = 0; - for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) { - indexed.push(listeners[i].zIndex); - if (listeners[i].zIndex < 0) { - queue[listeners[i].zIndex] = listeners[i]; - } - } - indexed.sort(numsort); - while (indexed[z] < 0) { - l = queue[indexed[z++]]; - out.push(l.apply(scope, args)); - if (stop) { - stop = oldstop; - return out; - } - } - for (i = 0; i < ii; i++) { - l = listeners[i]; - if ("zIndex" in l) { - if (l.zIndex == indexed[z]) { - out.push(l.apply(scope, args)); - if (stop) { - break; - } - do { - z++; - l = queue[indexed[z]]; - l && out.push(l.apply(scope, args)); - if (stop) { - break; - } - } while (l) - } else { - queue[l.zIndex] = l; - } - } else { - out.push(l.apply(scope, args)); - if (stop) { - break; - } - } - } - stop = oldstop; - current_event = ce; - return out; - }; - // Undocumented. Debug only. - eve._events = events; - /*\ - * eve.listeners - [ method ] - * Internal method which gives you array of all event handlers that will be triggered by the given `name`. - > Arguments - - name (string) name of the event, dot (`.`) or slash (`/`) separated - = (array) array of event handlers - \*/ - eve.listeners = function (name) { - var names = name.split(separator), - e = events, - item, - items, - k, - i, - ii, - j, - jj, - nes, - es = [e], - out = []; - for (i = 0, ii = names.length; i < ii; i++) { - nes = []; - for (j = 0, jj = es.length; j < jj; j++) { - e = es[j].n; - items = [e[names[i]], e[wildcard]]; - k = 2; - while (k--) { - item = items[k]; - if (item) { - nes.push(item); - out = out.concat(item.f || []); - } - } - } - es = nes; - } - return out; - }; - /*\ - * eve.on - [ method ] - ** - * Binds given event handler with a given name. You can use wildcards “`*`” for the names: - | eve.on("*.under.*", f); - | eve("mouse.under.floor"); // triggers f - * Use @eve to trigger the listener. - ** - > Arguments - ** - - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards - - f (function) event handler function - ** - = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. - > Example: - | eve.on("mouse", eatIt)(2); - | eve.on("mouse", scream); - | eve.on("mouse", catchIt)(1); - * This will ensure that `catchIt` function will be called before `eatIt`. - * - * If you want to put your handler before non-indexed handlers, specify a negative value. - * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”. - \*/ - eve.on = function (name, f) { - name = String(name); - if (typeof f != "function") { - return function () {}; - } - var names = name.split(comaseparator); - for (var i = 0, ii = names.length; i < ii; i++) { - (function (name) { - var names = name.split(separator), - e = events, - exist; - for (var i = 0, ii = names.length; i < ii; i++) { - e = e.n; - e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}}); - } - e.f = e.f || []; - for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { - exist = true; - break; - } - !exist && e.f.push(f); - }(names[i])); - } - return function (zIndex) { - if (+zIndex == +zIndex) { - f.zIndex = +zIndex; - } - }; - }; - /*\ - * eve.f - [ method ] - ** - * Returns function that will fire given event with optional arguments. - * Arguments that will be passed to the result function will be also - * concated to the list of final arguments. - | el.onclick = eve.f("click", 1, 2); - | eve.on("click", function (a, b, c) { - | console.log(a, b, c); // 1, 2, [event object] - | }); - > Arguments - - event (string) event name - - varargs (…) and any other arguments - = (function) possible event handler function - \*/ - eve.f = function (event) { - var attrs = [].slice.call(arguments, 1); - return function () { - eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0))); - }; - }; - /*\ - * eve.stop - [ method ] - ** - * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing. - \*/ - eve.stop = function () { - stop = 1; - }; - /*\ - * eve.nt - [ method ] - ** - * Could be used inside event handler to figure out actual name of the event. - ** - > Arguments - ** - - subname (string) #optional subname of the event - ** - = (string) name of the event, if `subname` is not specified - * or - = (boolean) `true`, if current event’s name contains `subname` - \*/ - eve.nt = function (subname) { - if (subname) { - return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event); - } - return current_event; - }; - /*\ - * eve.nts - [ method ] - ** - * Could be used inside event handler to figure out actual name of the event. - ** - ** - = (array) names of the event - \*/ - eve.nts = function () { - return current_event.split(separator); - }; - /*\ - * eve.off - [ method ] - ** - * Removes given function from the list of event listeners assigned to given name. - * If no arguments specified all the events will be cleared. - ** - > Arguments - ** - - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards - - f (function) event handler function - \*/ - /*\ - * eve.unbind - [ method ] - ** - * See @eve.off - \*/ - eve.off = eve.unbind = function (name, f) { - if (!name) { - eve._events = events = {n: {}}; - return; - } - var names = name.split(comaseparator); - if (names.length > 1) { - for (var i = 0, ii = names.length; i < ii; i++) { - eve.off(names[i], f); - } - return; - } - names = name.split(separator); - var e, - key, - splice, - i, ii, j, jj, - cur = [events]; - for (i = 0, ii = names.length; i < ii; i++) { - for (j = 0; j < cur.length; j += splice.length - 2) { - splice = [j, 1]; - e = cur[j].n; - if (names[i] != wildcard) { - if (e[names[i]]) { - splice.push(e[names[i]]); - } - } else { - for (key in e) if (e[has](key)) { - splice.push(e[key]); - } - } - cur.splice.apply(cur, splice); - } - } - for (i = 0, ii = cur.length; i < ii; i++) { - e = cur[i]; - while (e.n) { - if (f) { - if (e.f) { - for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) { - e.f.splice(j, 1); - break; - } - !e.f.length && delete e.f; - } - for (key in e.n) if (e.n[has](key) && e.n[key].f) { - var funcs = e.n[key].f; - for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) { - funcs.splice(j, 1); - break; - } - !funcs.length && delete e.n[key].f; - } - } else { - delete e.f; - for (key in e.n) if (e.n[has](key) && e.n[key].f) { - delete e.n[key].f; - } - } - e = e.n; - } - } - }; - /*\ - * eve.once - [ method ] - ** - * Binds given event handler with a given name to only run once then unbind itself. - | eve.once("login", f); - | eve("login"); // triggers f - | eve("login"); // no listeners - * Use @eve to trigger the listener. - ** - > Arguments - ** - - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards - - f (function) event handler function - ** - = (function) same return function as @eve.on - \*/ - eve.once = function (name, f) { - var f2 = function () { - eve.unbind(name, f2); - return f.apply(this, arguments); - }; - return eve.on(name, f2); - }; - /*\ - * eve.version - [ property (string) ] - ** - * Current version of the library. - \*/ - eve.version = version; - eve.toString = function () { - return "You are running Eve " + version; - }; - (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define === "function" && define.amd ? (define("eve", [], function() { return eve; })) : (glob.eve = eve)); -})(this); - -(function (glob, factory) { - // AMD support - if (typeof define == "function" && define.amd) { - // Define as an anonymous module - define(["eve"], function (eve) { - return factory(glob, eve); - }); - } else if (typeof exports != 'undefined') { - // Next for Node.js or CommonJS - var eve = require('eve'); - module.exports = factory(glob, eve); - } else { - // Browser globals (glob is window) - // Snap adds itself to window - factory(glob, glob.eve); - } -}(window || this, function (window, eve) { -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -var mina = (function (eve) { - var animations = {}, - requestAnimFrame = window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (callback) { - setTimeout(callback, 16); - }, - isArray = Array.isArray || function (a) { - return a instanceof Array || - Object.prototype.toString.call(a) == "[object Array]"; - }, - idgen = 0, - idprefix = "M" + (+new Date).toString(36), - ID = function () { - return idprefix + (idgen++).toString(36); - }, - diff = function (a, b, A, B) { - if (isArray(a)) { - res = []; - for (var i = 0, ii = a.length; i < ii; i++) { - res[i] = diff(a[i], b, A[i], B); - } - return res; - } - var dif = (A - a) / (B - b); - return function (bb) { - return a + dif * (bb - b); - }; - }, - timer = Date.now || function () { - return +new Date; - }, - sta = function (val) { - var a = this; - if (val == null) { - return a.s; - } - var ds = a.s - val; - a.b += a.dur * ds; - a.B += a.dur * ds; - a.s = val; - }, - speed = function (val) { - var a = this; - if (val == null) { - return a.spd; - } - a.spd = val; - }, - duration = function (val) { - var a = this; - if (val == null) { - return a.dur; - } - a.s = a.s * val / a.dur; - a.dur = val; - }, - stopit = function () { - var a = this; - delete animations[a.id]; - a.update(); - eve("mina.stop." + a.id, a); - }, - pause = function () { - var a = this; - if (a.pdif) { - return; - } - delete animations[a.id]; - a.update(); - a.pdif = a.get() - a.b; - }, - resume = function () { - var a = this; - if (!a.pdif) { - return; - } - a.b = a.get() - a.pdif; - delete a.pdif; - animations[a.id] = a; - }, - update = function () { - var a = this, - res; - if (isArray(a.start)) { - res = []; - for (var j = 0, jj = a.start.length; j < jj; j++) { - res[j] = +a.start[j] + - (a.end[j] - a.start[j]) * a.easing(a.s); - } - } else { - res = +a.start + (a.end - a.start) * a.easing(a.s); - } - a.set(res); - }, - frame = function () { - var len = 0; - for (var i in animations) if (animations.hasOwnProperty(i)) { - var a = animations[i], - b = a.get(), - res; - len++; - a.s = (b - a.b) / (a.dur / a.spd); - if (a.s >= 1) { - delete animations[i]; - a.s = 1; - len--; - (function (a) { - setTimeout(function () { - eve("mina.finish." + a.id, a); - }); - }(a)); - } - a.update(); - } - len && requestAnimFrame(frame); - }, - /*\ - * mina - [ method ] - ** - * Generic animation of numbers - ** - - a (number) start _slave_ number - - A (number) end _slave_ number - - b (number) start _master_ number (start time in general case) - - B (number) end _master_ number (end time in gereal case) - - get (function) getter of _master_ number (see @mina.time) - - set (function) setter of _slave_ number - - easing (function) #optional easing function, default is @mina.linear - = (object) animation descriptor - o { - o id (string) animation id, - o start (number) start _slave_ number, - o end (number) end _slave_ number, - o b (number) start _master_ number, - o s (number) animation status (0..1), - o dur (number) animation duration, - o spd (number) animation speed, - o get (function) getter of _master_ number (see @mina.time), - o set (function) setter of _slave_ number, - o easing (function) easing function, default is @mina.linear, - o status (function) status getter/setter, - o speed (function) speed getter/setter, - o duration (function) duration getter/setter, - o stop (function) animation stopper - o pause (function) pauses the animation - o resume (function) resumes the animation - o update (function) calles setter with the right value of the animation - o } - \*/ - mina = function (a, A, b, B, get, set, easing) { - var anim = { - id: ID(), - start: a, - end: A, - b: b, - s: 0, - dur: B - b, - spd: 1, - get: get, - set: set, - easing: easing || mina.linear, - status: sta, - speed: speed, - duration: duration, - stop: stopit, - pause: pause, - resume: resume, - update: update - }; - animations[anim.id] = anim; - var len = 0, i; - for (i in animations) if (animations.hasOwnProperty(i)) { - len++; - if (len == 2) { - break; - } - } - len == 1 && requestAnimFrame(frame); - return anim; - }; - /*\ - * mina.time - [ method ] - ** - * Returns the current time. Equivalent to: - | function () { - | return (new Date).getTime(); - | } - \*/ - mina.time = timer; - /*\ - * mina.getById - [ method ] - ** - * Returns an animation by its id - - id (string) animation's id - = (object) See @mina - \*/ - mina.getById = function (id) { - return animations[id] || null; - }; - - /*\ - * mina.linear - [ method ] - ** - * Default linear easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.linear = function (n) { - return n; - }; - /*\ - * mina.easeout - [ method ] - ** - * Easeout easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.easeout = function (n) { - return Math.pow(n, 1.7); - }; - /*\ - * mina.easein - [ method ] - ** - * Easein easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.easein = function (n) { - return Math.pow(n, .48); - }; - /*\ - * mina.easeinout - [ method ] - ** - * Easeinout easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.easeinout = function (n) { - if (n == 1) { - return 1; - } - if (n == 0) { - return 0; - } - var q = .48 - n / 1.04, - Q = Math.sqrt(.1734 + q * q), - x = Q - q, - X = Math.pow(Math.abs(x), 1 / 3) * (x < 0 ? -1 : 1), - y = -Q - q, - Y = Math.pow(Math.abs(y), 1 / 3) * (y < 0 ? -1 : 1), - t = X + Y + .5; - return (1 - t) * 3 * t * t + t * t * t; - }; - /*\ - * mina.backin - [ method ] - ** - * Backin easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.backin = function (n) { - if (n == 1) { - return 1; - } - var s = 1.70158; - return n * n * ((s + 1) * n - s); - }; - /*\ - * mina.backout - [ method ] - ** - * Backout easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.backout = function (n) { - if (n == 0) { - return 0; - } - n = n - 1; - var s = 1.70158; - return n * n * ((s + 1) * n + s) + 1; - }; - /*\ - * mina.elastic - [ method ] - ** - * Elastic easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.elastic = function (n) { - if (n == !!n) { - return n; - } - return Math.pow(2, -10 * n) * Math.sin((n - .075) * - (2 * Math.PI) / .3) + 1; - }; - /*\ - * mina.bounce - [ method ] - ** - * Bounce easing - - n (number) input 0..1 - = (number) output 0..1 - \*/ - mina.bounce = function (n) { - var s = 7.5625, - p = 2.75, - l; - if (n < (1 / p)) { - l = s * n * n; - } else { - if (n < (2 / p)) { - n -= (1.5 / p); - l = s * n * n + .75; - } else { - if (n < (2.5 / p)) { - n -= (2.25 / p); - l = s * n * n + .9375; - } else { - n -= (2.625 / p); - l = s * n * n + .984375; - } - } - } - return l; - }; - window.mina = mina; - return mina; -})(typeof eve == "undefined" ? function () {} : eve); -// Copyright (c) 2013 - 2015 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -var Snap = (function(root) { -Snap.version = "0.4.0"; -/*\ - * Snap - [ method ] - ** - * Creates a drawing surface or wraps existing SVG element. - ** - - width (number|string) width of surface - - height (number|string) height of surface - * or - - DOM (SVGElement) element to be wrapped into Snap structure - * or - - array (array) array of elements (will return set of elements) - * or - - query (string) CSS query selector - = (object) @Element -\*/ -function Snap(w, h) { - if (w) { - if (w.nodeType) { - return wrap(w); - } - if (is(w, "array") && Snap.set) { - return Snap.set.apply(Snap, w); - } - if (w instanceof Element) { - return w; - } - if (h == null) { - w = glob.doc.querySelector(String(w)); - return wrap(w); - } - } - w = w == null ? "100%" : w; - h = h == null ? "100%" : h; - return new Paper(w, h); -} -Snap.toString = function () { - return "Snap v" + this.version; -}; -Snap._ = {}; -var glob = { - win: root.window, - doc: root.window.document -}; -Snap._.glob = glob; -var has = "hasOwnProperty", - Str = String, - toFloat = parseFloat, - toInt = parseInt, - math = Math, - mmax = math.max, - mmin = math.min, - abs = math.abs, - pow = math.pow, - PI = math.PI, - round = math.round, - E = "", - S = " ", - objectToString = Object.prototype.toString, - ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, - colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i, - bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/, - reURLValue = /^url\(#?([^)]+)\)$/, - separator = Snap._.separator = /[,\s]+/, - whitespace = /[\s]/g, - commaSpaces = /[\s]*,[\s]*/, - hsrg = {hs: 1, rg: 1}, - pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig, - tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig, - pathValues = /(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/ig, - idgen = 0, - idprefix = "S" + (+new Date).toString(36), - ID = function (el) { - return (el && el.type ? el.type : E) + idprefix + (idgen++).toString(36); - }, - xlink = "http://www.w3.org/1999/xlink", - xmlns = "http://www.w3.org/2000/svg", - hub = {}, - URL = Snap.url = function (url) { - return "url('#" + url + "')"; - }; - -function $(el, attr) { - if (attr) { - if (el == "#text") { - el = glob.doc.createTextNode(attr.text || attr["#text"] || ""); - } - if (el == "#comment") { - el = glob.doc.createComment(attr.text || attr["#text"] || ""); - } - if (typeof el == "string") { - el = $(el); - } - if (typeof attr == "string") { - if (el.nodeType == 1) { - if (attr.substring(0, 6) == "xlink:") { - return el.getAttributeNS(xlink, attr.substring(6)); - } - if (attr.substring(0, 4) == "xml:") { - return el.getAttributeNS(xmlns, attr.substring(4)); - } - return el.getAttribute(attr); - } else if (attr == "text") { - return el.nodeValue; - } else { - return null; - } - } - if (el.nodeType == 1) { - for (var key in attr) if (attr[has](key)) { - var val = Str(attr[key]); - if (val) { - if (key.substring(0, 6) == "xlink:") { - el.setAttributeNS(xlink, key.substring(6), val); - } else if (key.substring(0, 4) == "xml:") { - el.setAttributeNS(xmlns, key.substring(4), val); - } else { - el.setAttribute(key, val); - } - } else { - el.removeAttribute(key); - } - } - } else if ("text" in attr) { - el.nodeValue = attr.text; - } - } else { - el = glob.doc.createElementNS(xmlns, el); - } - return el; -} -Snap._.$ = $; -Snap._.id = ID; -function getAttrs(el) { - var attrs = el.attributes, - name, - out = {}; - for (var i = 0; i < attrs.length; i++) { - if (attrs[i].namespaceURI == xlink) { - name = "xlink:"; - } else { - name = ""; - } - name += attrs[i].name; - out[name] = attrs[i].textContent; - } - return out; -} -function is(o, type) { - type = Str.prototype.toLowerCase.call(type); - if (type == "finite") { - return isFinite(o); - } - if (type == "array" && - (o instanceof Array || Array.isArray && Array.isArray(o))) { - return true; - } - return (type == "null" && o === null) || - (type == typeof o && o !== null) || - (type == "object" && o === Object(o)) || - objectToString.call(o).slice(8, -1).toLowerCase() == type; -} -/*\ - * Snap.format - [ method ] - ** - * Replaces construction of type `{}` to the corresponding argument - ** - - token (string) string to format - - json (object) object which properties are used as a replacement - = (string) formatted string - > Usage - | // this draws a rectangular shape equivalent to "M10,20h40v50h-40z" - | paper.path(Snap.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", { - | x: 10, - | y: 20, - | dim: { - | width: 40, - | height: 50, - | "negative width": -40 - | } - | })); -\*/ -Snap.format = (function () { - var tokenRegex = /\{([^\}]+)\}/g, - objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties - replacer = function (all, key, obj) { - var res = obj; - key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) { - name = name || quotedName; - if (res) { - if (name in res) { - res = res[name]; - } - typeof res == "function" && isFunc && (res = res()); - } - }); - res = (res == null || res == obj ? all : res) + ""; - return res; - }; - return function (str, obj) { - return Str(str).replace(tokenRegex, function (all, key) { - return replacer(all, key, obj); - }); - }; -})(); -function clone(obj) { - if (typeof obj == "function" || Object(obj) !== obj) { - return obj; - } - var res = new obj.constructor; - for (var key in obj) if (obj[has](key)) { - res[key] = clone(obj[key]); - } - return res; -} -Snap._.clone = clone; -function repush(array, item) { - for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { - return array.push(array.splice(i, 1)[0]); - } -} -function cacher(f, scope, postprocessor) { - function newf() { - var arg = Array.prototype.slice.call(arguments, 0), - args = arg.join("\u2400"), - cache = newf.cache = newf.cache || {}, - count = newf.count = newf.count || []; - if (cache[has](args)) { - repush(count, args); - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - count.length >= 1e3 && delete cache[count.shift()]; - count.push(args); - cache[args] = f.apply(scope, arg); - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - return newf; -} -Snap._.cacher = cacher; -function angle(x1, y1, x2, y2, x3, y3) { - if (x3 == null) { - var x = x1 - x2, - y = y1 - y2; - if (!x && !y) { - return 0; - } - return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360; - } else { - return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3); - } -} -function rad(deg) { - return deg % 360 * PI / 180; -} -function deg(rad) { - return rad * 180 / PI % 360; -} -function x_y() { - return this.x + S + this.y; -} -function x_y_w_h() { - return this.x + S + this.y + S + this.width + " \xd7 " + this.height; -} - -/*\ - * Snap.rad - [ method ] - ** - * Transform angle to radians - - deg (number) angle in degrees - = (number) angle in radians -\*/ -Snap.rad = rad; -/*\ - * Snap.deg - [ method ] - ** - * Transform angle to degrees - - rad (number) angle in radians - = (number) angle in degrees -\*/ -Snap.deg = deg; -/*\ - * Snap.sin - [ method ] - ** - * Equivalent to `Math.sin()` only works with degrees, not radians. - - angle (number) angle in degrees - = (number) sin -\*/ -Snap.sin = function (angle) { - return math.sin(Snap.rad(angle)); -}; -/*\ - * Snap.tan - [ method ] - ** - * Equivalent to `Math.tan()` only works with degrees, not radians. - - angle (number) angle in degrees - = (number) tan -\*/ -Snap.tan = function (angle) { - return math.tan(Snap.rad(angle)); -}; -/*\ - * Snap.cos - [ method ] - ** - * Equivalent to `Math.cos()` only works with degrees, not radians. - - angle (number) angle in degrees - = (number) cos -\*/ -Snap.cos = function (angle) { - return math.cos(Snap.rad(angle)); -}; -/*\ - * Snap.asin - [ method ] - ** - * Equivalent to `Math.asin()` only works with degrees, not radians. - - num (number) value - = (number) asin in degrees -\*/ -Snap.asin = function (num) { - return Snap.deg(math.asin(num)); -}; -/*\ - * Snap.acos - [ method ] - ** - * Equivalent to `Math.acos()` only works with degrees, not radians. - - num (number) value - = (number) acos in degrees -\*/ -Snap.acos = function (num) { - return Snap.deg(math.acos(num)); -}; -/*\ - * Snap.atan - [ method ] - ** - * Equivalent to `Math.atan()` only works with degrees, not radians. - - num (number) value - = (number) atan in degrees -\*/ -Snap.atan = function (num) { - return Snap.deg(math.atan(num)); -}; -/*\ - * Snap.atan2 - [ method ] - ** - * Equivalent to `Math.atan2()` only works with degrees, not radians. - - num (number) value - = (number) atan2 in degrees -\*/ -Snap.atan2 = function (num) { - return Snap.deg(math.atan2(num)); -}; -/*\ - * Snap.angle - [ method ] - ** - * Returns an angle between two or three points - > Parameters - - x1 (number) x coord of first point - - y1 (number) y coord of first point - - x2 (number) x coord of second point - - y2 (number) y coord of second point - - x3 (number) #optional x coord of third point - - y3 (number) #optional y coord of third point - = (number) angle in degrees -\*/ -Snap.angle = angle; -/*\ - * Snap.len - [ method ] - ** - * Returns distance between two points - > Parameters - - x1 (number) x coord of first point - - y1 (number) y coord of first point - - x2 (number) x coord of second point - - y2 (number) y coord of second point - = (number) distance -\*/ -Snap.len = function (x1, y1, x2, y2) { - return Math.sqrt(Snap.len2(x1, y1, x2, y2)); -}; -/*\ - * Snap.len2 - [ method ] - ** - * Returns squared distance between two points - > Parameters - - x1 (number) x coord of first point - - y1 (number) y coord of first point - - x2 (number) x coord of second point - - y2 (number) y coord of second point - = (number) distance -\*/ -Snap.len2 = function (x1, y1, x2, y2) { - return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); -}; -/*\ - * Snap.closestPoint - [ method ] - ** - * Returns closest point to a given one on a given path. - > Parameters - - path (Element) path element - - x (number) x coord of a point - - y (number) y coord of a point - = (object) in format - { - x (number) x coord of the point on the path - y (number) y coord of the point on the path - length (number) length of the path to the point - distance (number) distance from the given point to the path - } -\*/ -// Copied from http://bl.ocks.org/mbostock/8027637 -Snap.closestPoint = function (path, x, y) { - function distance2(p) { - var dx = p.x - x, - dy = p.y - y; - return dx * dx + dy * dy; - } - var pathNode = path.node, - pathLength = pathNode.getTotalLength(), - precision = pathLength / pathNode.pathSegList.numberOfItems * .125, - best, - bestLength, - bestDistance = Infinity; - - // linear scan for coarse approximation - for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { - if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { - best = scan, bestLength = scanLength, bestDistance = scanDistance; - } - } - - // binary search for precise estimate - precision *= .5; - while (precision > .5) { - var before, - after, - beforeLength, - afterLength, - beforeDistance, - afterDistance; - if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { - best = before, bestLength = beforeLength, bestDistance = beforeDistance; - } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { - best = after, bestLength = afterLength, bestDistance = afterDistance; - } else { - precision *= .5; - } - } - - best = { - x: best.x, - y: best.y, - length: bestLength, - distance: Math.sqrt(bestDistance) - }; - return best; -} -/*\ - * Snap.is - [ method ] - ** - * Handy replacement for the `typeof` operator - - o (…) any object or primitive - - type (string) name of the type, e.g., `string`, `function`, `number`, etc. - = (boolean) `true` if given value is of given type -\*/ -Snap.is = is; -/*\ - * Snap.snapTo - [ method ] - ** - * Snaps given value to given grid - - values (array|number) given array of values or step of the grid - - value (number) value to adjust - - tolerance (number) #optional maximum distance to the target value that would trigger the snap. Default is `10`. - = (number) adjusted value -\*/ -Snap.snapTo = function (values, value, tolerance) { - tolerance = is(tolerance, "finite") ? tolerance : 10; - if (is(values, "array")) { - var i = values.length; - while (i--) if (abs(values[i] - value) <= tolerance) { - return values[i]; - } - } else { - values = +values; - var rem = value % values; - if (rem < tolerance) { - return value - rem; - } - if (rem > values - tolerance) { - return value - rem + values; - } - } - return value; -}; -// Colour -/*\ - * Snap.getRGB - [ method ] - ** - * Parses color string as RGB object - - color (string) color string in one of the following formats: - #
    - #
  • Color name (red, green, cornflowerblue, etc)
  • - #
  • #••• — shortened HTML color: (#000, #fc0, etc.)
  • - #
  • #•••••• — full length HTML color: (#000000, #bd2300)
  • - #
  • rgb(•••, •••, •••) — red, green and blue channels values: (rgb(200, 100, 0))
  • - #
  • rgba(•••, •••, •••, •••) — also with opacity
  • - #
  • rgb(•••%, •••%, •••%) — same as above, but in %: (rgb(100%, 175%, 0%))
  • - #
  • rgba(•••%, •••%, •••%, •••%) — also with opacity
  • - #
  • hsb(•••, •••, •••) — hue, saturation and brightness values: (hsb(0.5, 0.25, 1))
  • - #
  • hsba(•••, •••, •••, •••) — also with opacity
  • - #
  • hsb(•••%, •••%, •••%) — same as above, but in %
  • - #
  • hsba(•••%, •••%, •••%, •••%) — also with opacity
  • - #
  • hsl(•••, •••, •••) — hue, saturation and luminosity values: (hsb(0.5, 0.25, 0.5))
  • - #
  • hsla(•••, •••, •••, •••) — also with opacity
  • - #
  • hsl(•••%, •••%, •••%) — same as above, but in %
  • - #
  • hsla(•••%, •••%, •••%, •••%) — also with opacity
  • - #
- * Note that `%` can be used any time: `rgb(20%, 255, 50%)`. - = (object) RGB object in the following format: - o { - o r (number) red, - o g (number) green, - o b (number) blue, - o hex (string) color in HTML/CSS format: #••••••, - o error (boolean) true if string can't be parsed - o } -\*/ -Snap.getRGB = cacher(function (colour) { - if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) { - return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; - } - if (colour == "none") { - return {r: -1, g: -1, b: -1, hex: "none", toString: rgbtoString}; - } - !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour)); - if (!colour) { - return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; - } - var res, - red, - green, - blue, - opacity, - t, - values, - rgb = colour.match(colourRegExp); - if (rgb) { - if (rgb[2]) { - blue = toInt(rgb[2].substring(5), 16); - green = toInt(rgb[2].substring(3, 5), 16); - red = toInt(rgb[2].substring(1, 3), 16); - } - if (rgb[3]) { - blue = toInt((t = rgb[3].charAt(3)) + t, 16); - green = toInt((t = rgb[3].charAt(2)) + t, 16); - red = toInt((t = rgb[3].charAt(1)) + t, 16); - } - if (rgb[4]) { - values = rgb[4].split(commaSpaces); - red = toFloat(values[0]); - values[0].slice(-1) == "%" && (red *= 2.55); - green = toFloat(values[1]); - values[1].slice(-1) == "%" && (green *= 2.55); - blue = toFloat(values[2]); - values[2].slice(-1) == "%" && (blue *= 2.55); - rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3])); - values[3] && values[3].slice(-1) == "%" && (opacity /= 100); - } - if (rgb[5]) { - values = rgb[5].split(commaSpaces); - red = toFloat(values[0]); - values[0].slice(-1) == "%" && (red /= 100); - green = toFloat(values[1]); - values[1].slice(-1) == "%" && (green /= 100); - blue = toFloat(values[2]); - values[2].slice(-1) == "%" && (blue /= 100); - (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); - rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3])); - values[3] && values[3].slice(-1) == "%" && (opacity /= 100); - return Snap.hsb2rgb(red, green, blue, opacity); - } - if (rgb[6]) { - values = rgb[6].split(commaSpaces); - red = toFloat(values[0]); - values[0].slice(-1) == "%" && (red /= 100); - green = toFloat(values[1]); - values[1].slice(-1) == "%" && (green /= 100); - blue = toFloat(values[2]); - values[2].slice(-1) == "%" && (blue /= 100); - (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); - rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3])); - values[3] && values[3].slice(-1) == "%" && (opacity /= 100); - return Snap.hsl2rgb(red, green, blue, opacity); - } - red = mmin(math.round(red), 255); - green = mmin(math.round(green), 255); - blue = mmin(math.round(blue), 255); - opacity = mmin(mmax(opacity, 0), 1); - rgb = {r: red, g: green, b: blue, toString: rgbtoString}; - rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1); - rgb.opacity = is(opacity, "finite") ? opacity : 1; - return rgb; - } - return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; -}, Snap); -/*\ - * Snap.hsb - [ method ] - ** - * Converts HSB values to a hex representation of the color - - h (number) hue - - s (number) saturation - - b (number) value or brightness - = (string) hex representation of the color -\*/ -Snap.hsb = cacher(function (h, s, b) { - return Snap.hsb2rgb(h, s, b).hex; -}); -/*\ - * Snap.hsl - [ method ] - ** - * Converts HSL values to a hex representation of the color - - h (number) hue - - s (number) saturation - - l (number) luminosity - = (string) hex representation of the color -\*/ -Snap.hsl = cacher(function (h, s, l) { - return Snap.hsl2rgb(h, s, l).hex; -}); -/*\ - * Snap.rgb - [ method ] - ** - * Converts RGB values to a hex representation of the color - - r (number) red - - g (number) green - - b (number) blue - = (string) hex representation of the color -\*/ -Snap.rgb = cacher(function (r, g, b, o) { - if (is(o, "finite")) { - var round = math.round; - return "rgba(" + [round(r), round(g), round(b), +o.toFixed(2)] + ")"; - } - return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1); -}); -var toHex = function (color) { - var i = glob.doc.getElementsByTagName("head")[0] || glob.doc.getElementsByTagName("svg")[0], - red = "rgb(255, 0, 0)"; - toHex = cacher(function (color) { - if (color.toLowerCase() == "red") { - return red; - } - i.style.color = red; - i.style.color = color; - var out = glob.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color"); - return out == red ? null : out; - }); - return toHex(color); -}, -hsbtoString = function () { - return "hsb(" + [this.h, this.s, this.b] + ")"; -}, -hsltoString = function () { - return "hsl(" + [this.h, this.s, this.l] + ")"; -}, -rgbtoString = function () { - return this.opacity == 1 || this.opacity == null ? - this.hex : - "rgba(" + [this.r, this.g, this.b, this.opacity] + ")"; -}, -prepareRGB = function (r, g, b) { - if (g == null && is(r, "object") && "r" in r && "g" in r && "b" in r) { - b = r.b; - g = r.g; - r = r.r; - } - if (g == null && is(r, string)) { - var clr = Snap.getRGB(r); - r = clr.r; - g = clr.g; - b = clr.b; - } - if (r > 1 || g > 1 || b > 1) { - r /= 255; - g /= 255; - b /= 255; - } - - return [r, g, b]; -}, -packageRGB = function (r, g, b, o) { - r = math.round(r * 255); - g = math.round(g * 255); - b = math.round(b * 255); - var rgb = { - r: r, - g: g, - b: b, - opacity: is(o, "finite") ? o : 1, - hex: Snap.rgb(r, g, b), - toString: rgbtoString - }; - is(o, "finite") && (rgb.opacity = o); - return rgb; -}; -/*\ - * Snap.color - [ method ] - ** - * Parses the color string and returns an object featuring the color's component values - - clr (string) color string in one of the supported formats (see @Snap.getRGB) - = (object) Combined RGB/HSB object in the following format: - o { - o r (number) red, - o g (number) green, - o b (number) blue, - o hex (string) color in HTML/CSS format: #••••••, - o error (boolean) `true` if string can't be parsed, - o h (number) hue, - o s (number) saturation, - o v (number) value (brightness), - o l (number) lightness - o } -\*/ -Snap.color = function (clr) { - var rgb; - if (is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) { - rgb = Snap.hsb2rgb(clr); - clr.r = rgb.r; - clr.g = rgb.g; - clr.b = rgb.b; - clr.opacity = 1; - clr.hex = rgb.hex; - } else if (is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) { - rgb = Snap.hsl2rgb(clr); - clr.r = rgb.r; - clr.g = rgb.g; - clr.b = rgb.b; - clr.opacity = 1; - clr.hex = rgb.hex; - } else { - if (is(clr, "string")) { - clr = Snap.getRGB(clr); - } - if (is(clr, "object") && "r" in clr && "g" in clr && "b" in clr && !("error" in clr)) { - rgb = Snap.rgb2hsl(clr); - clr.h = rgb.h; - clr.s = rgb.s; - clr.l = rgb.l; - rgb = Snap.rgb2hsb(clr); - clr.v = rgb.b; - } else { - clr = {hex: "none"}; - clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1; - clr.error = 1; - } - } - clr.toString = rgbtoString; - return clr; -}; -/*\ - * Snap.hsb2rgb - [ method ] - ** - * Converts HSB values to an RGB object - - h (number) hue - - s (number) saturation - - v (number) value or brightness - = (object) RGB object in the following format: - o { - o r (number) red, - o g (number) green, - o b (number) blue, - o hex (string) color in HTML/CSS format: #•••••• - o } -\*/ -Snap.hsb2rgb = function (h, s, v, o) { - if (is(h, "object") && "h" in h && "s" in h && "b" in h) { - v = h.b; - s = h.s; - o = h.o; - h = h.h; - } - h *= 360; - var R, G, B, X, C; - h = (h % 360) / 60; - C = v * s; - X = C * (1 - abs(h % 2 - 1)); - R = G = B = v - C; - - h = ~~h; - R += [C, X, 0, 0, X, C][h]; - G += [X, C, C, X, 0, 0][h]; - B += [0, 0, X, C, C, X][h]; - return packageRGB(R, G, B, o); -}; -/*\ - * Snap.hsl2rgb - [ method ] - ** - * Converts HSL values to an RGB object - - h (number) hue - - s (number) saturation - - l (number) luminosity - = (object) RGB object in the following format: - o { - o r (number) red, - o g (number) green, - o b (number) blue, - o hex (string) color in HTML/CSS format: #•••••• - o } -\*/ -Snap.hsl2rgb = function (h, s, l, o) { - if (is(h, "object") && "h" in h && "s" in h && "l" in h) { - l = h.l; - s = h.s; - h = h.h; - } - if (h > 1 || s > 1 || l > 1) { - h /= 360; - s /= 100; - l /= 100; - } - h *= 360; - var R, G, B, X, C; - h = (h % 360) / 60; - C = 2 * s * (l < .5 ? l : 1 - l); - X = C * (1 - abs(h % 2 - 1)); - R = G = B = l - C / 2; - - h = ~~h; - R += [C, X, 0, 0, X, C][h]; - G += [X, C, C, X, 0, 0][h]; - B += [0, 0, X, C, C, X][h]; - return packageRGB(R, G, B, o); -}; -/*\ - * Snap.rgb2hsb - [ method ] - ** - * Converts RGB values to an HSB object - - r (number) red - - g (number) green - - b (number) blue - = (object) HSB object in the following format: - o { - o h (number) hue, - o s (number) saturation, - o b (number) brightness - o } -\*/ -Snap.rgb2hsb = function (r, g, b) { - b = prepareRGB(r, g, b); - r = b[0]; - g = b[1]; - b = b[2]; - - var H, S, V, C; - V = mmax(r, g, b); - C = V - mmin(r, g, b); - H = (C == 0 ? null : - V == r ? (g - b) / C : - V == g ? (b - r) / C + 2 : - (r - g) / C + 4 - ); - H = ((H + 360) % 6) * 60 / 360; - S = C == 0 ? 0 : C / V; - return {h: H, s: S, b: V, toString: hsbtoString}; -}; -/*\ - * Snap.rgb2hsl - [ method ] - ** - * Converts RGB values to an HSL object - - r (number) red - - g (number) green - - b (number) blue - = (object) HSL object in the following format: - o { - o h (number) hue, - o s (number) saturation, - o l (number) luminosity - o } -\*/ -Snap.rgb2hsl = function (r, g, b) { - b = prepareRGB(r, g, b); - r = b[0]; - g = b[1]; - b = b[2]; - - var H, S, L, M, m, C; - M = mmax(r, g, b); - m = mmin(r, g, b); - C = M - m; - H = (C == 0 ? null : - M == r ? (g - b) / C : - M == g ? (b - r) / C + 2 : - (r - g) / C + 4); - H = ((H + 360) % 6) * 60 / 360; - L = (M + m) / 2; - S = (C == 0 ? 0 : - L < .5 ? C / (2 * L) : - C / (2 - 2 * L)); - return {h: H, s: S, l: L, toString: hsltoString}; -}; - -// Transformations -/*\ - * Snap.parsePathString - [ method ] - ** - * Utility method - ** - * Parses given path string into an array of arrays of path segments - - pathString (string|array) path string or array of segments (in the last case it is returned straight away) - = (array) array of segments -\*/ -Snap.parsePathString = function (pathString) { - if (!pathString) { - return null; - } - var pth = Snap.path(pathString); - if (pth.arr) { - return Snap.path.clone(pth.arr); - } - - var paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0}, - data = []; - if (is(pathString, "array") && is(pathString[0], "array")) { // rough assumption - data = Snap.path.clone(pathString); - } - if (!data.length) { - Str(pathString).replace(pathCommand, function (a, b, c) { - var params = [], - name = b.toLowerCase(); - c.replace(pathValues, function (a, b) { - b && params.push(+b); - }); - if (name == "m" && params.length > 2) { - data.push([b].concat(params.splice(0, 2))); - name = "l"; - b = b == "m" ? "l" : "L"; - } - if (name == "o" && params.length == 1) { - data.push([b, params[0]]); - } - if (name == "r") { - data.push([b].concat(params)); - } else while (params.length >= paramCounts[name]) { - data.push([b].concat(params.splice(0, paramCounts[name]))); - if (!paramCounts[name]) { - break; - } - } - }); - } - data.toString = Snap.path.toString; - pth.arr = Snap.path.clone(data); - return data; -}; -/*\ - * Snap.parseTransformString - [ method ] - ** - * Utility method - ** - * Parses given transform string into an array of transformations - - TString (string|array) transform string or array of transformations (in the last case it is returned straight away) - = (array) array of transformations -\*/ -var parseTransformString = Snap.parseTransformString = function (TString) { - if (!TString) { - return null; - } - var paramCounts = {r: 3, s: 4, t: 2, m: 6}, - data = []; - if (is(TString, "array") && is(TString[0], "array")) { // rough assumption - data = Snap.path.clone(TString); - } - if (!data.length) { - Str(TString).replace(tCommand, function (a, b, c) { - var params = [], - name = b.toLowerCase(); - c.replace(pathValues, function (a, b) { - b && params.push(+b); - }); - data.push([b].concat(params)); - }); - } - data.toString = Snap.path.toString; - return data; -}; -function svgTransform2string(tstr) { - var res = []; - tstr = tstr.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g, function (all, name, params) { - params = params.split(/\s*,\s*|\s+/); - if (name == "rotate" && params.length == 1) { - params.push(0, 0); - } - if (name == "scale") { - if (params.length > 2) { - params = params.slice(0, 2); - } else if (params.length == 2) { - params.push(0, 0); - } - if (params.length == 1) { - params.push(params[0], 0, 0); - } - } - if (name == "skewX") { - res.push(["m", 1, 0, math.tan(rad(params[0])), 1, 0, 0]); - } else if (name == "skewY") { - res.push(["m", 1, math.tan(rad(params[0])), 0, 1, 0, 0]); - } else { - res.push([name.charAt(0)].concat(params)); - } - return all; - }); - return res; -} -Snap._.svgTransform2string = svgTransform2string; -Snap._.rgTransform = /^[a-z][\s]*-?\.?\d/i; -function transform2matrix(tstr, bbox) { - var tdata = parseTransformString(tstr), - m = new Snap.Matrix; - if (tdata) { - for (var i = 0, ii = tdata.length; i < ii; i++) { - var t = tdata[i], - tlen = t.length, - command = Str(t[0]).toLowerCase(), - absolute = t[0] != command, - inver = absolute ? m.invert() : 0, - x1, - y1, - x2, - y2, - bb; - if (command == "t" && tlen == 2){ - m.translate(t[1], 0); - } else if (command == "t" && tlen == 3) { - if (absolute) { - x1 = inver.x(0, 0); - y1 = inver.y(0, 0); - x2 = inver.x(t[1], t[2]); - y2 = inver.y(t[1], t[2]); - m.translate(x2 - x1, y2 - y1); - } else { - m.translate(t[1], t[2]); - } - } else if (command == "r") { - if (tlen == 2) { - bb = bb || bbox; - m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2); - } else if (tlen == 4) { - if (absolute) { - x2 = inver.x(t[2], t[3]); - y2 = inver.y(t[2], t[3]); - m.rotate(t[1], x2, y2); - } else { - m.rotate(t[1], t[2], t[3]); - } - } - } else if (command == "s") { - if (tlen == 2 || tlen == 3) { - bb = bb || bbox; - m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2); - } else if (tlen == 4) { - if (absolute) { - x2 = inver.x(t[2], t[3]); - y2 = inver.y(t[2], t[3]); - m.scale(t[1], t[1], x2, y2); - } else { - m.scale(t[1], t[1], t[2], t[3]); - } - } else if (tlen == 5) { - if (absolute) { - x2 = inver.x(t[3], t[4]); - y2 = inver.y(t[3], t[4]); - m.scale(t[1], t[2], x2, y2); - } else { - m.scale(t[1], t[2], t[3], t[4]); - } - } - } else if (command == "m" && tlen == 7) { - m.add(t[1], t[2], t[3], t[4], t[5], t[6]); - } - } - } - return m; -} -Snap._.transform2matrix = transform2matrix; -Snap._unit2px = unit2px; -var contains = glob.doc.contains || glob.doc.compareDocumentPosition ? - function (a, b) { - var adown = a.nodeType == 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a == bup || !!(bup && bup.nodeType == 1 && ( - adown.contains ? - adown.contains(bup) : - a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 - )); - } : - function (a, b) { - if (b) { - while (b) { - b = b.parentNode; - if (b == a) { - return true; - } - } - } - return false; - }; -function getSomeDefs(el) { - var p = (el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) || - (el.node.parentNode && wrap(el.node.parentNode)) || - Snap.select("svg") || - Snap(0, 0), - pdefs = p.select("defs"), - defs = pdefs == null ? false : pdefs.node; - if (!defs) { - defs = make("defs", p.node).node; - } - return defs; -} -function getSomeSVG(el) { - return el.node.ownerSVGElement && wrap(el.node.ownerSVGElement) || Snap.select("svg"); -} -Snap._.getSomeDefs = getSomeDefs; -Snap._.getSomeSVG = getSomeSVG; -function unit2px(el, name, value) { - var svg = getSomeSVG(el).node, - out = {}, - mgr = svg.querySelector(".svg---mgr"); - if (!mgr) { - mgr = $("rect"); - $(mgr, {x: -9e9, y: -9e9, width: 10, height: 10, "class": "svg---mgr", fill: "none"}); - svg.appendChild(mgr); - } - function getW(val) { - if (val == null) { - return E; - } - if (val == +val) { - return val; - } - $(mgr, {width: val}); - try { - return mgr.getBBox().width; - } catch (e) { - return 0; - } - } - function getH(val) { - if (val == null) { - return E; - } - if (val == +val) { - return val; - } - $(mgr, {height: val}); - try { - return mgr.getBBox().height; - } catch (e) { - return 0; - } - } - function set(nam, f) { - if (name == null) { - out[nam] = f(el.attr(nam) || 0); - } else if (nam == name) { - out = f(value == null ? el.attr(nam) || 0 : value); - } - } - switch (el.type) { - case "rect": - set("rx", getW); - set("ry", getH); - case "image": - set("width", getW); - set("height", getH); - case "text": - set("x", getW); - set("y", getH); - break; - case "circle": - set("cx", getW); - set("cy", getH); - set("r", getW); - break; - case "ellipse": - set("cx", getW); - set("cy", getH); - set("rx", getW); - set("ry", getH); - break; - case "line": - set("x1", getW); - set("x2", getW); - set("y1", getH); - set("y2", getH); - break; - case "marker": - set("refX", getW); - set("markerWidth", getW); - set("refY", getH); - set("markerHeight", getH); - break; - case "radialGradient": - set("fx", getW); - set("fy", getH); - break; - case "tspan": - set("dx", getW); - set("dy", getH); - break; - default: - set(name, getW); - } - svg.removeChild(mgr); - return out; -} -/*\ - * Snap.select - [ method ] - ** - * Wraps a DOM element specified by CSS selector as @Element - - query (string) CSS selector of the element - = (Element) the current element -\*/ -Snap.select = function (query) { - query = Str(query).replace(/([^\\]):/g, "$1\\:"); - return wrap(glob.doc.querySelector(query)); -}; -/*\ - * Snap.selectAll - [ method ] - ** - * Wraps DOM elements specified by CSS selector as set or array of @Element - - query (string) CSS selector of the element - = (Element) the current element -\*/ -Snap.selectAll = function (query) { - var nodelist = glob.doc.querySelectorAll(query), - set = (Snap.set || Array)(); - for (var i = 0; i < nodelist.length; i++) { - set.push(wrap(nodelist[i])); - } - return set; -}; - -function add2group(list) { - if (!is(list, "array")) { - list = Array.prototype.slice.call(arguments, 0); - } - var i = 0, - j = 0, - node = this.node; - while (this[i]) delete this[i++]; - for (i = 0; i < list.length; i++) { - if (list[i].type == "set") { - list[i].forEach(function (el) { - node.appendChild(el.node); - }); - } else { - node.appendChild(list[i].node); - } - } - var children = node.childNodes; - for (i = 0; i < children.length; i++) { - this[j++] = wrap(children[i]); - } - return this; -} -// Hub garbage collector every 10s -setInterval(function () { - for (var key in hub) if (hub[has](key)) { - var el = hub[key], - node = el.node; - if (el.type != "svg" && !node.ownerSVGElement || el.type == "svg" && (!node.parentNode || "ownerSVGElement" in node.parentNode && !node.ownerSVGElement)) { - delete hub[key]; - } - } -}, 1e4); -function Element(el) { - if (el.snap in hub) { - return hub[el.snap]; - } - var svg; - try { - svg = el.ownerSVGElement; - } catch(e) {} - /*\ - * Element.node - [ property (object) ] - ** - * Gives you a reference to the DOM object, so you can assign event handlers or just mess around. - > Usage - | // draw a circle at coordinate 10,10 with radius of 10 - | var c = paper.circle(10, 10, 10); - | c.node.onclick = function () { - | c.attr("fill", "red"); - | }; - \*/ - this.node = el; - if (svg) { - this.paper = new Paper(svg); - } - /*\ - * Element.type - [ property (string) ] - ** - * SVG tag name of the given element. - \*/ - this.type = el.tagName || el.nodeName; - var id = this.id = ID(this); - this.anims = {}; - this._ = { - transform: [] - }; - el.snap = id; - hub[id] = this; - if (this.type == "g") { - this.add = add2group; - } - if (this.type in {g: 1, mask: 1, pattern: 1, symbol: 1}) { - for (var method in Paper.prototype) if (Paper.prototype[has](method)) { - this[method] = Paper.prototype[method]; - } - } -} - /*\ - * Element.attr - [ method ] - ** - * Gets or sets given attributes of the element. - ** - - params (object) contains key-value pairs of attributes you want to set - * or - - param (string) name of the attribute - = (Element) the current element - * or - = (string) value of attribute - > Usage - | el.attr({ - | fill: "#fc0", - | stroke: "#000", - | strokeWidth: 2, // CamelCase... - | "fill-opacity": 0.5, // or dash-separated names - | width: "*=2" // prefixed values - | }); - | console.log(el.attr("fill")); // #fc0 - * Prefixed values in format `"+=10"` supported. All four operations - * (`+`, `-`, `*` and `/`) could be used. Optionally you can use units for `+` - * and `-`: `"+=2em"`. - \*/ - Element.prototype.attr = function (params, value) { - var el = this, - node = el.node; - if (!params) { - if (node.nodeType != 1) { - return { - text: node.nodeValue - }; - } - var attr = node.attributes, - out = {}; - for (var i = 0, ii = attr.length; i < ii; i++) { - out[attr[i].nodeName] = attr[i].nodeValue; - } - return out; - } - if (is(params, "string")) { - if (arguments.length > 1) { - var json = {}; - json[params] = value; - params = json; - } else { - return eve("snap.util.getattr." + params, el).firstDefined(); - } - } - for (var att in params) { - if (params[has](att)) { - eve("snap.util.attr." + att, el, params[att]); - } - } - return el; - }; -/*\ - * Snap.parse - [ method ] - ** - * Parses SVG fragment and converts it into a @Fragment - ** - - svg (string) SVG string - = (Fragment) the @Fragment -\*/ -Snap.parse = function (svg) { - var f = glob.doc.createDocumentFragment(), - full = true, - div = glob.doc.createElement("div"); - svg = Str(svg); - if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) { - svg = "" + svg + ""; - full = false; - } - div.innerHTML = svg; - svg = div.getElementsByTagName("svg")[0]; - if (svg) { - if (full) { - f = svg; - } else { - while (svg.firstChild) { - f.appendChild(svg.firstChild); - } - } - } - return new Fragment(f); -}; -function Fragment(frag) { - this.node = frag; -} -/*\ - * Snap.fragment - [ method ] - ** - * Creates a DOM fragment from a given list of elements or strings - ** - - varargs (…) SVG string - = (Fragment) the @Fragment -\*/ -Snap.fragment = function () { - var args = Array.prototype.slice.call(arguments, 0), - f = glob.doc.createDocumentFragment(); - for (var i = 0, ii = args.length; i < ii; i++) { - var item = args[i]; - if (item.node && item.node.nodeType) { - f.appendChild(item.node); - } - if (item.nodeType) { - f.appendChild(item); - } - if (typeof item == "string") { - f.appendChild(Snap.parse(item).node); - } - } - return new Fragment(f); -}; - -function make(name, parent) { - var res = $(name); - parent.appendChild(res); - var el = wrap(res); - return el; -} -function Paper(w, h) { - var res, - desc, - defs, - proto = Paper.prototype; - if (w && w.tagName == "svg") { - if (w.snap in hub) { - return hub[w.snap]; - } - var doc = w.ownerDocument; - res = new Element(w); - desc = w.getElementsByTagName("desc")[0]; - defs = w.getElementsByTagName("defs")[0]; - if (!desc) { - desc = $("desc"); - desc.appendChild(doc.createTextNode("Created with Snap")); - res.node.appendChild(desc); - } - if (!defs) { - defs = $("defs"); - res.node.appendChild(defs); - } - res.defs = defs; - for (var key in proto) if (proto[has](key)) { - res[key] = proto[key]; - } - res.paper = res.root = res; - } else { - res = make("svg", glob.doc.body); - $(res.node, { - height: h, - version: 1.1, - width: w, - xmlns: xmlns - }); - } - return res; -} -function wrap(dom) { - if (!dom) { - return dom; - } - if (dom instanceof Element || dom instanceof Fragment) { - return dom; - } - if (dom.tagName && dom.tagName.toLowerCase() == "svg") { - return new Paper(dom); - } - if (dom.tagName && dom.tagName.toLowerCase() == "object" && dom.type == "image/svg+xml") { - return new Paper(dom.contentDocument.getElementsByTagName("svg")[0]); - } - return new Element(dom); -} - -Snap._.make = make; -Snap._.wrap = wrap; -/*\ - * Paper.el - [ method ] - ** - * Creates an element on paper with a given name and no attributes - ** - - name (string) tag name - - attr (object) attributes - = (Element) the current element - > Usage - | var c = paper.circle(10, 10, 10); // is the same as... - | var c = paper.el("circle").attr({ - | cx: 10, - | cy: 10, - | r: 10 - | }); - | // and the same as - | var c = paper.el("circle", { - | cx: 10, - | cy: 10, - | r: 10 - | }); -\*/ -Paper.prototype.el = function (name, attr) { - var el = make(name, this.node); - attr && el.attr(attr); - return el; -}; -/*\ - * Element.children - [ method ] - ** - * Returns array of all the children of the element. - = (array) array of Elements -\*/ -Element.prototype.children = function () { - var out = [], - ch = this.node.childNodes; - for (var i = 0, ii = ch.length; i < ii; i++) { - out[i] = Snap(ch[i]); - } - return out; -}; -function jsonFiller(root, o) { - for (var i = 0, ii = root.length; i < ii; i++) { - var item = { - type: root[i].type, - attr: root[i].attr() - }, - children = root[i].children(); - o.push(item); - if (children.length) { - jsonFiller(children, item.childNodes = []); - } - } -} -/*\ - * Element.toJSON - [ method ] - ** - * Returns object representation of the given element and all its children. - = (object) in format - o { - o type (string) this.type, - o attr (object) attributes map, - o childNodes (array) optional array of children in the same format - o } -\*/ -Element.prototype.toJSON = function () { - var out = []; - jsonFiller([this], out); - return out[0]; -}; -// default -eve.on("snap.util.getattr", function () { - var att = eve.nt(); - att = att.substring(att.lastIndexOf(".") + 1); - var css = att.replace(/[A-Z]/g, function (letter) { - return "-" + letter.toLowerCase(); - }); - if (cssAttr[has](css)) { - return this.node.ownerDocument.defaultView.getComputedStyle(this.node, null).getPropertyValue(css); - } else { - return $(this.node, att); - } -}); -var cssAttr = { - "alignment-baseline": 0, - "baseline-shift": 0, - "clip": 0, - "clip-path": 0, - "clip-rule": 0, - "color": 0, - "color-interpolation": 0, - "color-interpolation-filters": 0, - "color-profile": 0, - "color-rendering": 0, - "cursor": 0, - "direction": 0, - "display": 0, - "dominant-baseline": 0, - "enable-background": 0, - "fill": 0, - "fill-opacity": 0, - "fill-rule": 0, - "filter": 0, - "flood-color": 0, - "flood-opacity": 0, - "font": 0, - "font-family": 0, - "font-size": 0, - "font-size-adjust": 0, - "font-stretch": 0, - "font-style": 0, - "font-variant": 0, - "font-weight": 0, - "glyph-orientation-horizontal": 0, - "glyph-orientation-vertical": 0, - "image-rendering": 0, - "kerning": 0, - "letter-spacing": 0, - "lighting-color": 0, - "marker": 0, - "marker-end": 0, - "marker-mid": 0, - "marker-start": 0, - "mask": 0, - "opacity": 0, - "overflow": 0, - "pointer-events": 0, - "shape-rendering": 0, - "stop-color": 0, - "stop-opacity": 0, - "stroke": 0, - "stroke-dasharray": 0, - "stroke-dashoffset": 0, - "stroke-linecap": 0, - "stroke-linejoin": 0, - "stroke-miterlimit": 0, - "stroke-opacity": 0, - "stroke-width": 0, - "text-anchor": 0, - "text-decoration": 0, - "text-rendering": 0, - "unicode-bidi": 0, - "visibility": 0, - "word-spacing": 0, - "writing-mode": 0 -}; - -eve.on("snap.util.attr", function (value) { - var att = eve.nt(), - attr = {}; - att = att.substring(att.lastIndexOf(".") + 1); - attr[att] = value; - var style = att.replace(/-(\w)/gi, function (all, letter) { - return letter.toUpperCase(); - }), - css = att.replace(/[A-Z]/g, function (letter) { - return "-" + letter.toLowerCase(); - }); - if (cssAttr[has](css)) { - this.node.style[style] = value == null ? E : value; - } else { - $(this.node, attr); - } -}); -(function (proto) {}(Paper.prototype)); - -// simple ajax -/*\ - * Snap.ajax - [ method ] - ** - * Simple implementation of Ajax - ** - - url (string) URL - - postData (object|string) data for post request - - callback (function) callback - - scope (object) #optional scope of callback - * or - - url (string) URL - - callback (function) callback - - scope (object) #optional scope of callback - = (XMLHttpRequest) the XMLHttpRequest object, just in case -\*/ -Snap.ajax = function (url, postData, callback, scope){ - var req = new XMLHttpRequest, - id = ID(); - if (req) { - if (is(postData, "function")) { - scope = callback; - callback = postData; - postData = null; - } else if (is(postData, "object")) { - var pd = []; - for (var key in postData) if (postData.hasOwnProperty(key)) { - pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key])); - } - postData = pd.join("&"); - } - req.open((postData ? "POST" : "GET"), url, true); - if (postData) { - req.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - } - if (callback) { - eve.once("snap.ajax." + id + ".0", callback); - eve.once("snap.ajax." + id + ".200", callback); - eve.once("snap.ajax." + id + ".304", callback); - } - req.onreadystatechange = function() { - if (req.readyState != 4) return; - eve("snap.ajax." + id + "." + req.status, scope, req); - }; - if (req.readyState == 4) { - return req; - } - req.send(postData); - return req; - } -}; -/*\ - * Snap.load - [ method ] - ** - * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX) - ** - - url (string) URL - - callback (function) callback - - scope (object) #optional scope of callback -\*/ -Snap.load = function (url, callback, scope) { - Snap.ajax(url, function (req) { - var f = Snap.parse(req.responseText); - scope ? callback.call(scope, f) : callback(f); - }); -}; -var getOffset = function (elem) { - var box = elem.getBoundingClientRect(), - doc = elem.ownerDocument, - body = doc.body, - docElem = doc.documentElement, - clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, - top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, - left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; - return { - y: top, - x: left - }; -}; -/*\ - * Snap.getElementByPoint - [ method ] - ** - * Returns you topmost element under given point. - ** - = (object) Snap element object - - x (number) x coordinate from the top left corner of the window - - y (number) y coordinate from the top left corner of the window - > Usage - | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"}); -\*/ -Snap.getElementByPoint = function (x, y) { - var paper = this, - svg = paper.canvas, - target = glob.doc.elementFromPoint(x, y); - if (glob.win.opera && target.tagName == "svg") { - var so = getOffset(target), - sr = target.createSVGRect(); - sr.x = x - so.x; - sr.y = y - so.y; - sr.width = sr.height = 1; - var hits = target.getIntersectionList(sr, null); - if (hits.length) { - target = hits[hits.length - 1]; - } - } - if (!target) { - return null; - } - return wrap(target); -}; -/*\ - * Snap.plugin - [ method ] - ** - * Let you write plugins. You pass in a function with five arguments, like this: - | Snap.plugin(function (Snap, Element, Paper, global, Fragment) { - | Snap.newmethod = function () {}; - | Element.prototype.newmethod = function () {}; - | Paper.prototype.newmethod = function () {}; - | }); - * Inside the function you have access to all main objects (and their - * prototypes). This allow you to extend anything you want. - ** - - f (function) your plugin body -\*/ -Snap.plugin = function (f) { - f(Snap, Element, Paper, glob, Fragment); -}; -glob.win.Snap = Snap; -return Snap; -}(window || this)); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var elproto = Element.prototype, - is = Snap.is, - Str = String, - unit2px = Snap._unit2px, - $ = Snap._.$, - make = Snap._.make, - getSomeDefs = Snap._.getSomeDefs, - has = "hasOwnProperty", - wrap = Snap._.wrap; - /*\ - * Element.getBBox - [ method ] - ** - * Returns the bounding box descriptor for the given element - ** - = (object) bounding box descriptor: - o { - o cx: (number) x of the center, - o cy: (number) x of the center, - o h: (number) height, - o height: (number) height, - o path: (string) path command for the box, - o r0: (number) radius of a circle that fully encloses the box, - o r1: (number) radius of the smallest circle that can be enclosed, - o r2: (number) radius of the largest circle that can be enclosed, - o vb: (string) box as a viewbox command, - o w: (number) width, - o width: (number) width, - o x2: (number) x of the right side, - o x: (number) x of the left side, - o y2: (number) y of the bottom edge, - o y: (number) y of the top edge - o } - \*/ - elproto.getBBox = function (isWithoutTransform) { - if (!Snap.Matrix || !Snap.path) { - return this.node.getBBox(); - } - var el = this, - m = new Snap.Matrix; - if (el.removed) { - return Snap._.box(); - } - while (el.type == "use") { - if (!isWithoutTransform) { - m = m.add(el.transform().localMatrix.translate(el.attr("x") || 0, el.attr("y") || 0)); - } - if (el.original) { - el = el.original; - } else { - var href = el.attr("xlink:href"); - el = el.original = el.node.ownerDocument.getElementById(href.substring(href.indexOf("#") + 1)); - } - } - var _ = el._, - pathfinder = Snap.path.get[el.type] || Snap.path.get.deflt; - try { - if (isWithoutTransform) { - _.bboxwt = pathfinder ? Snap.path.getBBox(el.realPath = pathfinder(el)) : Snap._.box(el.node.getBBox()); - return Snap._.box(_.bboxwt); - } else { - el.realPath = pathfinder(el); - el.matrix = el.transform().localMatrix; - _.bbox = Snap.path.getBBox(Snap.path.map(el.realPath, m.add(el.matrix))); - return Snap._.box(_.bbox); - } - } catch (e) { - // Firefox doesn’t give you bbox of hidden element - return Snap._.box(); - } - }; - var propString = function () { - return this.string; - }; - function extractTransform(el, tstr) { - if (tstr == null) { - var doReturn = true; - if (el.type == "linearGradient" || el.type == "radialGradient") { - tstr = el.node.getAttribute("gradientTransform"); - } else if (el.type == "pattern") { - tstr = el.node.getAttribute("patternTransform"); - } else { - tstr = el.node.getAttribute("transform"); - } - if (!tstr) { - return new Snap.Matrix; - } - tstr = Snap._.svgTransform2string(tstr); - } else { - if (!Snap._.rgTransform.test(tstr)) { - tstr = Snap._.svgTransform2string(tstr); - } else { - tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || ""); - } - if (is(tstr, "array")) { - tstr = Snap.path ? Snap.path.toString.call(tstr) : Str(tstr); - } - el._.transform = tstr; - } - var m = Snap._.transform2matrix(tstr, el.getBBox(1)); - if (doReturn) { - return m; - } else { - el.matrix = m; - } - } - /*\ - * Element.transform - [ method ] - ** - * Gets or sets transformation of the element - ** - - tstr (string) transform string in Snap or SVG format - = (Element) the current element - * or - = (object) transformation descriptor: - o { - o string (string) transform string, - o globalMatrix (Matrix) matrix of all transformations applied to element or its parents, - o localMatrix (Matrix) matrix of transformations applied only to the element, - o diffMatrix (Matrix) matrix of difference between global and local transformations, - o global (string) global transformation as string, - o local (string) local transformation as string, - o toString (function) returns `string` property - o } - \*/ - elproto.transform = function (tstr) { - var _ = this._; - if (tstr == null) { - var papa = this, - global = new Snap.Matrix(this.node.getCTM()), - local = extractTransform(this), - ms = [local], - m = new Snap.Matrix, - i, - localString = local.toTransformString(), - string = Str(local) == Str(this.matrix) ? - Str(_.transform) : localString; - while (papa.type != "svg" && (papa = papa.parent())) { - ms.push(extractTransform(papa)); - } - i = ms.length; - while (i--) { - m.add(ms[i]); - } - return { - string: string, - globalMatrix: global, - totalMatrix: m, - localMatrix: local, - diffMatrix: global.clone().add(local.invert()), - global: global.toTransformString(), - total: m.toTransformString(), - local: localString, - toString: propString - }; - } - if (tstr instanceof Snap.Matrix) { - this.matrix = tstr; - this._.transform = tstr.toTransformString(); - } else { - extractTransform(this, tstr); - } - - if (this.node) { - if (this.type == "linearGradient" || this.type == "radialGradient") { - $(this.node, {gradientTransform: this.matrix}); - } else if (this.type == "pattern") { - $(this.node, {patternTransform: this.matrix}); - } else { - $(this.node, {transform: this.matrix}); - } - } - - return this; - }; - /*\ - * Element.parent - [ method ] - ** - * Returns the element's parent - ** - = (Element) the parent element - \*/ - elproto.parent = function () { - return wrap(this.node.parentNode); - }; - /*\ - * Element.append - [ method ] - ** - * Appends the given element to current one - ** - - el (Element|Set) element to append - = (Element) the parent element - \*/ - /*\ - * Element.add - [ method ] - ** - * See @Element.append - \*/ - elproto.append = elproto.add = function (el) { - if (el) { - if (el.type == "set") { - var it = this; - el.forEach(function (el) { - it.add(el); - }); - return this; - } - el = wrap(el); - this.node.appendChild(el.node); - el.paper = this.paper; - } - return this; - }; - /*\ - * Element.appendTo - [ method ] - ** - * Appends the current element to the given one - ** - - el (Element) parent element to append to - = (Element) the child element - \*/ - elproto.appendTo = function (el) { - if (el) { - el = wrap(el); - el.append(this); - } - return this; - }; - /*\ - * Element.prepend - [ method ] - ** - * Prepends the given element to the current one - ** - - el (Element) element to prepend - = (Element) the parent element - \*/ - elproto.prepend = function (el) { - if (el) { - if (el.type == "set") { - var it = this, - first; - el.forEach(function (el) { - if (first) { - first.after(el); - } else { - it.prepend(el); - } - first = el; - }); - return this; - } - el = wrap(el); - var parent = el.parent(); - this.node.insertBefore(el.node, this.node.firstChild); - this.add && this.add(); - el.paper = this.paper; - this.parent() && this.parent().add(); - parent && parent.add(); - } - return this; - }; - /*\ - * Element.prependTo - [ method ] - ** - * Prepends the current element to the given one - ** - - el (Element) parent element to prepend to - = (Element) the child element - \*/ - elproto.prependTo = function (el) { - el = wrap(el); - el.prepend(this); - return this; - }; - /*\ - * Element.before - [ method ] - ** - * Inserts given element before the current one - ** - - el (Element) element to insert - = (Element) the parent element - \*/ - elproto.before = function (el) { - if (el.type == "set") { - var it = this; - el.forEach(function (el) { - var parent = el.parent(); - it.node.parentNode.insertBefore(el.node, it.node); - parent && parent.add(); - }); - this.parent().add(); - return this; - } - el = wrap(el); - var parent = el.parent(); - this.node.parentNode.insertBefore(el.node, this.node); - this.parent() && this.parent().add(); - parent && parent.add(); - el.paper = this.paper; - return this; - }; - /*\ - * Element.after - [ method ] - ** - * Inserts given element after the current one - ** - - el (Element) element to insert - = (Element) the parent element - \*/ - elproto.after = function (el) { - el = wrap(el); - var parent = el.parent(); - if (this.node.nextSibling) { - this.node.parentNode.insertBefore(el.node, this.node.nextSibling); - } else { - this.node.parentNode.appendChild(el.node); - } - this.parent() && this.parent().add(); - parent && parent.add(); - el.paper = this.paper; - return this; - }; - /*\ - * Element.insertBefore - [ method ] - ** - * Inserts the element after the given one - ** - - el (Element) element next to whom insert to - = (Element) the parent element - \*/ - elproto.insertBefore = function (el) { - el = wrap(el); - var parent = this.parent(); - el.node.parentNode.insertBefore(this.node, el.node); - this.paper = el.paper; - parent && parent.add(); - el.parent() && el.parent().add(); - return this; - }; - /*\ - * Element.insertAfter - [ method ] - ** - * Inserts the element after the given one - ** - - el (Element) element next to whom insert to - = (Element) the parent element - \*/ - elproto.insertAfter = function (el) { - el = wrap(el); - var parent = this.parent(); - el.node.parentNode.insertBefore(this.node, el.node.nextSibling); - this.paper = el.paper; - parent && parent.add(); - el.parent() && el.parent().add(); - return this; - }; - /*\ - * Element.remove - [ method ] - ** - * Removes element from the DOM - = (Element) the detached element - \*/ - elproto.remove = function () { - var parent = this.parent(); - this.node.parentNode && this.node.parentNode.removeChild(this.node); - delete this.paper; - this.removed = true; - parent && parent.add(); - return this; - }; - /*\ - * Element.select - [ method ] - ** - * Gathers the nested @Element matching the given set of CSS selectors - ** - - query (string) CSS selector - = (Element) result of query selection - \*/ - elproto.select = function (query) { - return wrap(this.node.querySelector(query)); - }; - /*\ - * Element.selectAll - [ method ] - ** - * Gathers nested @Element objects matching the given set of CSS selectors - ** - - query (string) CSS selector - = (Set|array) result of query selection - \*/ - elproto.selectAll = function (query) { - var nodelist = this.node.querySelectorAll(query), - set = (Snap.set || Array)(); - for (var i = 0; i < nodelist.length; i++) { - set.push(wrap(nodelist[i])); - } - return set; - }; - /*\ - * Element.asPX - [ method ] - ** - * Returns given attribute of the element as a `px` value (not %, em, etc.) - ** - - attr (string) attribute name - - value (string) #optional attribute value - = (Element) result of query selection - \*/ - elproto.asPX = function (attr, value) { - if (value == null) { - value = this.attr(attr); - } - return +unit2px(this, attr, value); - }; - // SIERRA Element.use(): I suggest adding a note about how to access the original element the returned instantiates. It's a part of SVG with which ordinary web developers may be least familiar. - /*\ - * Element.use - [ method ] - ** - * Creates a `` element linked to the current element - ** - = (Element) the `` element - \*/ - elproto.use = function () { - var use, - id = this.node.id; - if (!id) { - id = this.id; - $(this.node, { - id: id - }); - } - if (this.type == "linearGradient" || this.type == "radialGradient" || - this.type == "pattern") { - use = make(this.type, this.node.parentNode); - } else { - use = make("use", this.node.parentNode); - } - $(use.node, { - "xlink:href": "#" + id - }); - use.original = this; - return use; - }; - function fixids(el) { - var els = el.selectAll("*"), - it, - url = /^\s*url\(("|'|)(.*)\1\)\s*$/, - ids = [], - uses = {}; - function urltest(it, name) { - var val = $(it.node, name); - val = val && val.match(url); - val = val && val[2]; - if (val && val.charAt() == "#") { - val = val.substring(1); - } else { - return; - } - if (val) { - uses[val] = (uses[val] || []).concat(function (id) { - var attr = {}; - attr[name] = URL(id); - $(it.node, attr); - }); - } - } - function linktest(it) { - var val = $(it.node, "xlink:href"); - if (val && val.charAt() == "#") { - val = val.substring(1); - } else { - return; - } - if (val) { - uses[val] = (uses[val] || []).concat(function (id) { - it.attr("xlink:href", "#" + id); - }); - } - } - for (var i = 0, ii = els.length; i < ii; i++) { - it = els[i]; - urltest(it, "fill"); - urltest(it, "stroke"); - urltest(it, "filter"); - urltest(it, "mask"); - urltest(it, "clip-path"); - linktest(it); - var oldid = $(it.node, "id"); - if (oldid) { - $(it.node, {id: it.id}); - ids.push({ - old: oldid, - id: it.id - }); - } - } - for (i = 0, ii = ids.length; i < ii; i++) { - var fs = uses[ids[i].old]; - if (fs) { - for (var j = 0, jj = fs.length; j < jj; j++) { - fs[j](ids[i].id); - } - } - } - } - /*\ - * Element.clone - [ method ] - ** - * Creates a clone of the element and inserts it after the element - ** - = (Element) the clone - \*/ - elproto.clone = function () { - var clone = wrap(this.node.cloneNode(true)); - if ($(clone.node, "id")) { - $(clone.node, {id: clone.id}); - } - fixids(clone); - clone.insertAfter(this); - return clone; - }; - /*\ - * Element.toDefs - [ method ] - ** - * Moves element to the shared `` area - ** - = (Element) the element - \*/ - elproto.toDefs = function () { - var defs = getSomeDefs(this); - defs.appendChild(this.node); - return this; - }; - /*\ - * Element.toPattern - [ method ] - ** - * Creates a `` element from the current element - ** - * To create a pattern you have to specify the pattern rect: - - x (string|number) - - y (string|number) - - width (string|number) - - height (string|number) - = (Element) the `` element - * You can use pattern later on as an argument for `fill` attribute: - | var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({ - | fill: "none", - | stroke: "#bada55", - | strokeWidth: 5 - | }).pattern(0, 0, 10, 10), - | c = paper.circle(200, 200, 100); - | c.attr({ - | fill: p - | }); - \*/ - elproto.pattern = elproto.toPattern = function (x, y, width, height) { - var p = make("pattern", getSomeDefs(this)); - if (x == null) { - x = this.getBBox(); - } - if (is(x, "object") && "x" in x) { - y = x.y; - width = x.width; - height = x.height; - x = x.x; - } - $(p.node, { - x: x, - y: y, - width: width, - height: height, - patternUnits: "userSpaceOnUse", - id: p.id, - viewBox: [x, y, width, height].join(" ") - }); - p.node.appendChild(this.node); - return p; - }; -// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path. -// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values? - /*\ - * Element.marker - [ method ] - ** - * Creates a `` element from the current element - ** - * To create a marker you have to specify the bounding rect and reference point: - - x (number) - - y (number) - - width (number) - - height (number) - - refX (number) - - refY (number) - = (Element) the `` element - * You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end. - \*/ - // TODO add usage for markers - elproto.marker = function (x, y, width, height, refX, refY) { - var p = make("marker", getSomeDefs(this)); - if (x == null) { - x = this.getBBox(); - } - if (is(x, "object") && "x" in x) { - y = x.y; - width = x.width; - height = x.height; - refX = x.refX || x.cx; - refY = x.refY || x.cy; - x = x.x; - } - $(p.node, { - viewBox: [x, y, width, height].join(" "), - markerWidth: width, - markerHeight: height, - orient: "auto", - refX: refX || 0, - refY: refY || 0, - id: p.id - }); - p.node.appendChild(this.node); - return p; - }; - // animation - function slice(from, to, f) { - return function (arr) { - var res = arr.slice(from, to); - if (res.length == 1) { - res = res[0]; - } - return f ? f(res) : res; - }; - } - var Animation = function (attr, ms, easing, callback) { - if (typeof easing == "function" && !easing.length) { - callback = easing; - easing = mina.linear; - } - this.attr = attr; - this.dur = ms; - easing && (this.easing = easing); - callback && (this.callback = callback); - }; - Snap._.Animation = Animation; - /*\ - * Snap.animation - [ method ] - ** - * Creates an animation object - ** - - attr (object) attributes of final destination - - duration (number) duration of the animation, in milliseconds - - easing (function) #optional one of easing functions of @mina or custom one - - callback (function) #optional callback function that fires when animation ends - = (object) animation object - \*/ - Snap.animation = function (attr, ms, easing, callback) { - return new Animation(attr, ms, easing, callback); - }; - /*\ - * Element.inAnim - [ method ] - ** - * Returns a set of animations that may be able to manipulate the current element - ** - = (object) in format: - o { - o anim (object) animation object, - o mina (object) @mina object, - o curStatus (number) 0..1 — status of the animation: 0 — just started, 1 — just finished, - o status (function) gets or sets the status of the animation, - o stop (function) stops the animation - o } - \*/ - elproto.inAnim = function () { - var el = this, - res = []; - for (var id in el.anims) if (el.anims[has](id)) { - (function (a) { - res.push({ - anim: new Animation(a._attrs, a.dur, a.easing, a._callback), - mina: a, - curStatus: a.status(), - status: function (val) { - return a.status(val); - }, - stop: function () { - a.stop(); - } - }); - }(el.anims[id])); - } - return res; - }; - /*\ - * Snap.animate - [ method ] - ** - * Runs generic animation of one number into another with a caring function - ** - - from (number|array) number or array of numbers - - to (number|array) number or array of numbers - - setter (function) caring function that accepts one number argument - - duration (number) duration, in milliseconds - - easing (function) #optional easing function from @mina or custom - - callback (function) #optional callback function to execute when animation ends - = (object) animation object in @mina format - o { - o id (string) animation id, consider it read-only, - o duration (function) gets or sets the duration of the animation, - o easing (function) easing, - o speed (function) gets or sets the speed of the animation, - o status (function) gets or sets the status of the animation, - o stop (function) stops the animation - o } - | var rect = Snap().rect(0, 0, 10, 10); - | Snap.animate(0, 10, function (val) { - | rect.attr({ - | x: val - | }); - | }, 1000); - | // in given context is equivalent to - | rect.animate({x: 10}, 1000); - \*/ - Snap.animate = function (from, to, setter, ms, easing, callback) { - if (typeof easing == "function" && !easing.length) { - callback = easing; - easing = mina.linear; - } - var now = mina.time(), - anim = mina(from, to, now, now + ms, mina.time, setter, easing); - callback && eve.once("mina.finish." + anim.id, callback); - return anim; - }; - /*\ - * Element.stop - [ method ] - ** - * Stops all the animations for the current element - ** - = (Element) the current element - \*/ - elproto.stop = function () { - var anims = this.inAnim(); - for (var i = 0, ii = anims.length; i < ii; i++) { - anims[i].stop(); - } - return this; - }; - /*\ - * Element.animate - [ method ] - ** - * Animates the given attributes of the element - ** - - attrs (object) key-value pairs of destination attributes - - duration (number) duration of the animation in milliseconds - - easing (function) #optional easing function from @mina or custom - - callback (function) #optional callback function that executes when the animation ends - = (Element) the current element - \*/ - elproto.animate = function (attrs, ms, easing, callback) { - if (typeof easing == "function" && !easing.length) { - callback = easing; - easing = mina.linear; - } - if (attrs instanceof Animation) { - callback = attrs.callback; - easing = attrs.easing; - ms = attrs.dur; - attrs = attrs.attr; - } - var fkeys = [], tkeys = [], keys = {}, from, to, f, eq, - el = this; - for (var key in attrs) if (attrs[has](key)) { - if (el.equal) { - eq = el.equal(key, Str(attrs[key])); - from = eq.from; - to = eq.to; - f = eq.f; - } else { - from = +el.attr(key); - to = +attrs[key]; - } - var len = is(from, "array") ? from.length : 1; - keys[key] = slice(fkeys.length, fkeys.length + len, f); - fkeys = fkeys.concat(from); - tkeys = tkeys.concat(to); - } - var now = mina.time(), - anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) { - var attr = {}; - for (var key in keys) if (keys[has](key)) { - attr[key] = keys[key](val); - } - el.attr(attr); - }, easing); - el.anims[anim.id] = anim; - anim._attrs = attrs; - anim._callback = callback; - eve("snap.animcreated." + el.id, anim); - eve.once("mina.finish." + anim.id, function () { - delete el.anims[anim.id]; - callback && callback.call(el); - }); - eve.once("mina.stop." + anim.id, function () { - delete el.anims[anim.id]; - }); - return el; - }; - var eldata = {}; - /*\ - * Element.data - [ method ] - ** - * Adds or retrieves given value associated with given key. (Don’t confuse - * with `data-` attributes) - * - * See also @Element.removeData - - key (string) key to store data - - value (any) #optional value to store - = (object) @Element - * or, if value is not specified: - = (any) value - > Usage - | for (var i = 0, i < 5, i++) { - | paper.circle(10 + 15 * i, 10, 10) - | .attr({fill: "#000"}) - | .data("i", i) - | .click(function () { - | alert(this.data("i")); - | }); - | } - \*/ - elproto.data = function (key, value) { - var data = eldata[this.id] = eldata[this.id] || {}; - if (arguments.length == 0){ - eve("snap.data.get." + this.id, this, data, null); - return data; - } - if (arguments.length == 1) { - if (Snap.is(key, "object")) { - for (var i in key) if (key[has](i)) { - this.data(i, key[i]); - } - return this; - } - eve("snap.data.get." + this.id, this, data[key], key); - return data[key]; - } - data[key] = value; - eve("snap.data.set." + this.id, this, value, key); - return this; - }; - /*\ - * Element.removeData - [ method ] - ** - * Removes value associated with an element by given key. - * If key is not provided, removes all the data of the element. - - key (string) #optional key - = (object) @Element - \*/ - elproto.removeData = function (key) { - if (key == null) { - eldata[this.id] = {}; - } else { - eldata[this.id] && delete eldata[this.id][key]; - } - return this; - }; - /*\ - * Element.outerSVG - [ method ] - ** - * Returns SVG code for the element, equivalent to HTML's `outerHTML`. - * - * See also @Element.innerSVG - = (string) SVG code for the element - \*/ - /*\ - * Element.toString - [ method ] - ** - * See @Element.outerSVG - \*/ - elproto.outerSVG = elproto.toString = toString(1); - /*\ - * Element.innerSVG - [ method ] - ** - * Returns SVG code for the element's contents, equivalent to HTML's `innerHTML` - = (string) SVG code for the element - \*/ - elproto.innerSVG = toString(); - function toString(type) { - return function () { - var res = type ? "<" + this.type : "", - attr = this.node.attributes, - chld = this.node.childNodes; - if (type) { - for (var i = 0, ii = attr.length; i < ii; i++) { - res += " " + attr[i].name + '="' + - attr[i].value.replace(/"/g, '\\"') + '"'; - } - } - if (chld.length) { - type && (res += ">"); - for (i = 0, ii = chld.length; i < ii; i++) { - if (chld[i].nodeType == 3) { - res += chld[i].nodeValue; - } else if (chld[i].nodeType == 1) { - res += wrap(chld[i]).toString(); - } - } - type && (res += ""); - } else { - type && (res += "/>"); - } - return res; - }; - } - elproto.toDataURL = function () { - if (window && window.btoa) { - var bb = this.getBBox(), - svg = Snap.format('{contents}', { - x: +bb.x.toFixed(3), - y: +bb.y.toFixed(3), - width: +bb.width.toFixed(3), - height: +bb.height.toFixed(3), - contents: this.outerSVG() - }); - return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svg))); - } - }; - /*\ - * Fragment.select - [ method ] - ** - * See @Element.select - \*/ - Fragment.prototype.select = elproto.select; - /*\ - * Fragment.selectAll - [ method ] - ** - * See @Element.selectAll - \*/ - Fragment.prototype.selectAll = elproto.selectAll; -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var objectToString = Object.prototype.toString, - Str = String, - math = Math, - E = ""; - function Matrix(a, b, c, d, e, f) { - if (b == null && objectToString.call(a) == "[object SVGMatrix]") { - this.a = a.a; - this.b = a.b; - this.c = a.c; - this.d = a.d; - this.e = a.e; - this.f = a.f; - return; - } - if (a != null) { - this.a = +a; - this.b = +b; - this.c = +c; - this.d = +d; - this.e = +e; - this.f = +f; - } else { - this.a = 1; - this.b = 0; - this.c = 0; - this.d = 1; - this.e = 0; - this.f = 0; - } - } - (function (matrixproto) { - /*\ - * Matrix.add - [ method ] - ** - * Adds the given matrix to existing one - - a (number) - - b (number) - - c (number) - - d (number) - - e (number) - - f (number) - * or - - matrix (object) @Matrix - \*/ - matrixproto.add = function (a, b, c, d, e, f) { - var out = [[], [], []], - m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], - matrix = [[a, c, e], [b, d, f], [0, 0, 1]], - x, y, z, res; - - if (a && a instanceof Matrix) { - matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; - } - - for (x = 0; x < 3; x++) { - for (y = 0; y < 3; y++) { - res = 0; - for (z = 0; z < 3; z++) { - res += m[x][z] * matrix[z][y]; - } - out[x][y] = res; - } - } - this.a = out[0][0]; - this.b = out[1][0]; - this.c = out[0][1]; - this.d = out[1][1]; - this.e = out[0][2]; - this.f = out[1][2]; - return this; - }; - /*\ - * Matrix.invert - [ method ] - ** - * Returns an inverted version of the matrix - = (object) @Matrix - \*/ - matrixproto.invert = function () { - var me = this, - x = me.a * me.d - me.b * me.c; - return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x); - }; - /*\ - * Matrix.clone - [ method ] - ** - * Returns a copy of the matrix - = (object) @Matrix - \*/ - matrixproto.clone = function () { - return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); - }; - /*\ - * Matrix.translate - [ method ] - ** - * Translate the matrix - - x (number) horizontal offset distance - - y (number) vertical offset distance - \*/ - matrixproto.translate = function (x, y) { - return this.add(1, 0, 0, 1, x, y); - }; - /*\ - * Matrix.scale - [ method ] - ** - * Scales the matrix - - x (number) amount to be scaled, with `1` resulting in no change - - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.) - - cx (number) #optional horizontal origin point from which to scale - - cy (number) #optional vertical origin point from which to scale - * Default cx, cy is the middle point of the element. - \*/ - matrixproto.scale = function (x, y, cx, cy) { - y == null && (y = x); - (cx || cy) && this.add(1, 0, 0, 1, cx, cy); - this.add(x, 0, 0, y, 0, 0); - (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy); - return this; - }; - /*\ - * Matrix.rotate - [ method ] - ** - * Rotates the matrix - - a (number) angle of rotation, in degrees - - x (number) horizontal origin point from which to rotate - - y (number) vertical origin point from which to rotate - \*/ - matrixproto.rotate = function (a, x, y) { - a = Snap.rad(a); - x = x || 0; - y = y || 0; - var cos = +math.cos(a).toFixed(9), - sin = +math.sin(a).toFixed(9); - this.add(cos, sin, -sin, cos, x, y); - return this.add(1, 0, 0, 1, -x, -y); - }; - /*\ - * Matrix.x - [ method ] - ** - * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y - - x (number) - - y (number) - = (number) x - \*/ - matrixproto.x = function (x, y) { - return x * this.a + y * this.c + this.e; - }; - /*\ - * Matrix.y - [ method ] - ** - * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x - - x (number) - - y (number) - = (number) y - \*/ - matrixproto.y = function (x, y) { - return x * this.b + y * this.d + this.f; - }; - matrixproto.get = function (i) { - return +this[Str.fromCharCode(97 + i)].toFixed(4); - }; - matrixproto.toString = function () { - return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")"; - }; - matrixproto.offset = function () { - return [this.e.toFixed(4), this.f.toFixed(4)]; - }; - function norm(a) { - return a[0] * a[0] + a[1] * a[1]; - } - function normalize(a) { - var mag = math.sqrt(norm(a)); - a[0] && (a[0] /= mag); - a[1] && (a[1] /= mag); - } - /*\ - * Matrix.determinant - [ method ] - ** - * Finds determinant of the given matrix. - = (number) determinant - \*/ - matrixproto.determinant = function () { - return this.a * this.d - this.b * this.c; - }; - /*\ - * Matrix.split - [ method ] - ** - * Splits matrix into primitive transformations - = (object) in format: - o dx (number) translation by x - o dy (number) translation by y - o scalex (number) scale by x - o scaley (number) scale by y - o shear (number) shear - o rotate (number) rotation in deg - o isSimple (boolean) could it be represented via simple transformations - \*/ - matrixproto.split = function () { - var out = {}; - // translation - out.dx = this.e; - out.dy = this.f; - - // scale and shear - var row = [[this.a, this.c], [this.b, this.d]]; - out.scalex = math.sqrt(norm(row[0])); - normalize(row[0]); - - out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; - row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; - - out.scaley = math.sqrt(norm(row[1])); - normalize(row[1]); - out.shear /= out.scaley; - - if (this.determinant() < 0) { - out.scalex = -out.scalex; - } - - // rotation - var sin = -row[0][1], - cos = row[1][1]; - if (cos < 0) { - out.rotate = Snap.deg(math.acos(cos)); - if (sin < 0) { - out.rotate = 360 - out.rotate; - } - } else { - out.rotate = Snap.deg(math.asin(sin)); - } - - out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate); - out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate; - out.noRotation = !+out.shear.toFixed(9) && !out.rotate; - return out; - }; - /*\ - * Matrix.toTransformString - [ method ] - ** - * Returns transform string that represents given matrix - = (string) transform string - \*/ - matrixproto.toTransformString = function (shorter) { - var s = shorter || this.split(); - if (!+s.shear.toFixed(9)) { - s.scalex = +s.scalex.toFixed(4); - s.scaley = +s.scaley.toFixed(4); - s.rotate = +s.rotate.toFixed(4); - return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) + - (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) + - (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E); - } else { - return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]; - } - }; - })(Matrix.prototype); - /*\ - * Snap.Matrix - [ method ] - ** - * Matrix constructor, extend on your own risk. - * To create matrices use @Snap.matrix. - \*/ - Snap.Matrix = Matrix; - /*\ - * Snap.matrix - [ method ] - ** - * Utility method - ** - * Returns a matrix based on the given parameters - - a (number) - - b (number) - - c (number) - - d (number) - - e (number) - - f (number) - * or - - svgMatrix (SVGMatrix) - = (object) @Matrix - \*/ - Snap.matrix = function (a, b, c, d, e, f) { - return new Matrix(a, b, c, d, e, f); - }; -}); -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var has = "hasOwnProperty", - make = Snap._.make, - wrap = Snap._.wrap, - is = Snap.is, - getSomeDefs = Snap._.getSomeDefs, - reURLValue = /^url\(#?([^)]+)\)$/, - $ = Snap._.$, - URL = Snap.url, - Str = String, - separator = Snap._.separator, - E = ""; - // Attributes event handlers - eve.on("snap.util.attr.mask", function (value) { - if (value instanceof Element || value instanceof Fragment) { - eve.stop(); - if (value instanceof Fragment && value.node.childNodes.length == 1) { - value = value.node.firstChild; - getSomeDefs(this).appendChild(value); - value = wrap(value); - } - if (value.type == "mask") { - var mask = value; - } else { - mask = make("mask", getSomeDefs(this)); - mask.node.appendChild(value.node); - } - !mask.node.id && $(mask.node, { - id: mask.id - }); - $(this.node, { - mask: URL(mask.id) - }); - } - }); - (function (clipIt) { - eve.on("snap.util.attr.clip", clipIt); - eve.on("snap.util.attr.clip-path", clipIt); - eve.on("snap.util.attr.clipPath", clipIt); - }(function (value) { - if (value instanceof Element || value instanceof Fragment) { - eve.stop(); - if (value.type == "clipPath") { - var clip = value; - } else { - clip = make("clipPath", getSomeDefs(this)); - clip.node.appendChild(value.node); - !clip.node.id && $(clip.node, { - id: clip.id - }); - } - $(this.node, { - "clip-path": URL(clip.node.id || clip.id) - }); - } - })); - function fillStroke(name) { - return function (value) { - eve.stop(); - if (value instanceof Fragment && value.node.childNodes.length == 1 && - (value.node.firstChild.tagName == "radialGradient" || - value.node.firstChild.tagName == "linearGradient" || - value.node.firstChild.tagName == "pattern")) { - value = value.node.firstChild; - getSomeDefs(this).appendChild(value); - value = wrap(value); - } - if (value instanceof Element) { - if (value.type == "radialGradient" || value.type == "linearGradient" - || value.type == "pattern") { - if (!value.node.id) { - $(value.node, { - id: value.id - }); - } - var fill = URL(value.node.id); - } else { - fill = value.attr(name); - } - } else { - fill = Snap.color(value); - if (fill.error) { - var grad = Snap(getSomeDefs(this).ownerSVGElement).gradient(value); - if (grad) { - if (!grad.node.id) { - $(grad.node, { - id: grad.id - }); - } - fill = URL(grad.node.id); - } else { - fill = value; - } - } else { - fill = Str(fill); - } - } - var attrs = {}; - attrs[name] = fill; - $(this.node, attrs); - this.node.style[name] = E; - }; - } - eve.on("snap.util.attr.fill", fillStroke("fill")); - eve.on("snap.util.attr.stroke", fillStroke("stroke")); - var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i; - eve.on("snap.util.grad.parse", function parseGrad(string) { - string = Str(string); - var tokens = string.match(gradrg); - if (!tokens) { - return null; - } - var type = tokens[1], - params = tokens[2], - stops = tokens[3]; - params = params.split(/\s*,\s*/).map(function (el) { - return +el == el ? +el : el; - }); - if (params.length == 1 && params[0] == 0) { - params = []; - } - stops = stops.split("-"); - stops = stops.map(function (el) { - el = el.split(":"); - var out = { - color: el[0] - }; - if (el[1]) { - out.offset = parseFloat(el[1]); - } - return out; - }); - return { - type: type, - params: params, - stops: stops - }; - }); - - eve.on("snap.util.attr.d", function (value) { - eve.stop(); - if (is(value, "array") && is(value[0], "array")) { - value = Snap.path.toString.call(value); - } - value = Str(value); - if (value.match(/[ruo]/i)) { - value = Snap.path.toAbsolute(value); - } - $(this.node, {d: value}); - })(-1); - eve.on("snap.util.attr.#text", function (value) { - eve.stop(); - value = Str(value); - var txt = glob.doc.createTextNode(value); - while (this.node.firstChild) { - this.node.removeChild(this.node.firstChild); - } - this.node.appendChild(txt); - })(-1); - eve.on("snap.util.attr.path", function (value) { - eve.stop(); - this.attr({d: value}); - })(-1); - eve.on("snap.util.attr.class", function (value) { - eve.stop(); - this.node.className.baseVal = value; - })(-1); - eve.on("snap.util.attr.viewBox", function (value) { - var vb; - if (is(value, "object") && "x" in value) { - vb = [value.x, value.y, value.width, value.height].join(" "); - } else if (is(value, "array")) { - vb = value.join(" "); - } else { - vb = value; - } - $(this.node, { - viewBox: vb - }); - eve.stop(); - })(-1); - eve.on("snap.util.attr.transform", function (value) { - this.transform(value); - eve.stop(); - })(-1); - eve.on("snap.util.attr.r", function (value) { - if (this.type == "rect") { - eve.stop(); - $(this.node, { - rx: value, - ry: value - }); - } - })(-1); - eve.on("snap.util.attr.textpath", function (value) { - eve.stop(); - if (this.type == "text") { - var id, tp, node; - if (!value && this.textPath) { - tp = this.textPath; - while (tp.node.firstChild) { - this.node.appendChild(tp.node.firstChild); - } - tp.remove(); - delete this.textPath; - return; - } - if (is(value, "string")) { - var defs = getSomeDefs(this), - path = wrap(defs.parentNode).path(value); - defs.appendChild(path.node); - id = path.id; - path.attr({id: id}); - } else { - value = wrap(value); - if (value instanceof Element) { - id = value.attr("id"); - if (!id) { - id = value.id; - value.attr({id: id}); - } - } - } - if (id) { - tp = this.textPath; - node = this.node; - if (tp) { - tp.attr({"xlink:href": "#" + id}); - } else { - tp = $("textPath", { - "xlink:href": "#" + id - }); - while (node.firstChild) { - tp.appendChild(node.firstChild); - } - node.appendChild(tp); - this.textPath = wrap(tp); - } - } - } - })(-1); - eve.on("snap.util.attr.text", function (value) { - if (this.type == "text") { - var i = 0, - node = this.node, - tuner = function (chunk) { - var out = $("tspan"); - if (is(chunk, "array")) { - for (var i = 0; i < chunk.length; i++) { - out.appendChild(tuner(chunk[i])); - } - } else { - out.appendChild(glob.doc.createTextNode(chunk)); - } - out.normalize && out.normalize(); - return out; - }; - while (node.firstChild) { - node.removeChild(node.firstChild); - } - var tuned = tuner(value); - while (tuned.firstChild) { - node.appendChild(tuned.firstChild); - } - } - eve.stop(); - })(-1); - function setFontSize(value) { - eve.stop(); - if (value == +value) { - value += "px"; - } - this.node.style.fontSize = value; - } - eve.on("snap.util.attr.fontSize", setFontSize)(-1); - eve.on("snap.util.attr.font-size", setFontSize)(-1); - - - eve.on("snap.util.getattr.transform", function () { - eve.stop(); - return this.transform(); - })(-1); - eve.on("snap.util.getattr.textpath", function () { - eve.stop(); - return this.textPath; - })(-1); - // Markers - (function () { - function getter(end) { - return function () { - eve.stop(); - var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end); - if (style == "none") { - return style; - } else { - return Snap(glob.doc.getElementById(style.match(reURLValue)[1])); - } - }; - } - function setter(end) { - return function (value) { - eve.stop(); - var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1); - if (value == "" || !value) { - this.node.style[name] = "none"; - return; - } - if (value.type == "marker") { - var id = value.node.id; - if (!id) { - $(value.node, {id: value.id}); - } - this.node.style[name] = URL(id); - return; - } - }; - } - eve.on("snap.util.getattr.marker-end", getter("end"))(-1); - eve.on("snap.util.getattr.markerEnd", getter("end"))(-1); - eve.on("snap.util.getattr.marker-start", getter("start"))(-1); - eve.on("snap.util.getattr.markerStart", getter("start"))(-1); - eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1); - eve.on("snap.util.getattr.markerMid", getter("mid"))(-1); - eve.on("snap.util.attr.marker-end", setter("end"))(-1); - eve.on("snap.util.attr.markerEnd", setter("end"))(-1); - eve.on("snap.util.attr.marker-start", setter("start"))(-1); - eve.on("snap.util.attr.markerStart", setter("start"))(-1); - eve.on("snap.util.attr.marker-mid", setter("mid"))(-1); - eve.on("snap.util.attr.markerMid", setter("mid"))(-1); - }()); - eve.on("snap.util.getattr.r", function () { - if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) { - eve.stop(); - return $(this.node, "rx"); - } - })(-1); - function textExtract(node) { - var out = []; - var children = node.childNodes; - for (var i = 0, ii = children.length; i < ii; i++) { - var chi = children[i]; - if (chi.nodeType == 3) { - out.push(chi.nodeValue); - } - if (chi.tagName == "tspan") { - if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) { - out.push(chi.firstChild.nodeValue); - } else { - out.push(textExtract(chi)); - } - } - } - return out; - } - eve.on("snap.util.getattr.text", function () { - if (this.type == "text" || this.type == "tspan") { - eve.stop(); - var out = textExtract(this.node); - return out.length == 1 ? out[0] : out; - } - })(-1); - eve.on("snap.util.getattr.#text", function () { - return this.node.textContent; - })(-1); - eve.on("snap.util.getattr.viewBox", function () { - eve.stop(); - var vb = $(this.node, "viewBox"); - if (vb) { - vb = vb.split(separator); - return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]); - } else { - return; - } - })(-1); - eve.on("snap.util.getattr.points", function () { - var p = $(this.node, "points"); - eve.stop(); - if (p) { - return p.split(separator); - } else { - return; - } - })(-1); - eve.on("snap.util.getattr.path", function () { - var p = $(this.node, "d"); - eve.stop(); - return p; - })(-1); - eve.on("snap.util.getattr.class", function () { - return this.node.className.baseVal; - })(-1); - function getFontSize() { - eve.stop(); - return this.node.style.fontSize; - } - eve.on("snap.util.getattr.fontSize", getFontSize)(-1); - eve.on("snap.util.getattr.font-size", getFontSize)(-1); -}); - -// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var rgNotSpace = /\S+/g, - rgBadSpace = /[\t\r\n\f]/g, - rgTrim = /(^\s+|\s+$)/g, - Str = String, - elproto = Element.prototype; - /*\ - * Element.addClass - [ method ] - ** - * Adds given class name or list of class names to the element. - - value (string) class name or space separated list of class names - ** - = (Element) original element. - \*/ - elproto.addClass = function (value) { - var classes = Str(value || "").match(rgNotSpace) || [], - elem = this.node, - className = elem.className.baseVal, - curClasses = className.match(rgNotSpace) || [], - j, - pos, - clazz, - finalValue; - - if (classes.length) { - j = 0; - while ((clazz = classes[j++])) { - pos = curClasses.indexOf(clazz); - if (!~pos) { - curClasses.push(clazz); - } - } - - finalValue = curClasses.join(" "); - if (className != finalValue) { - elem.className.baseVal = finalValue; - } - } - return this; - }; - /*\ - * Element.removeClass - [ method ] - ** - * Removes given class name or list of class names from the element. - - value (string) class name or space separated list of class names - ** - = (Element) original element. - \*/ - elproto.removeClass = function (value) { - var classes = Str(value || "").match(rgNotSpace) || [], - elem = this.node, - className = elem.className.baseVal, - curClasses = className.match(rgNotSpace) || [], - j, - pos, - clazz, - finalValue; - if (curClasses.length) { - j = 0; - while ((clazz = classes[j++])) { - pos = curClasses.indexOf(clazz); - if (~pos) { - curClasses.splice(pos, 1); - } - } - - finalValue = curClasses.join(" "); - if (className != finalValue) { - elem.className.baseVal = finalValue; - } - } - return this; - }; - /*\ - * Element.hasClass - [ method ] - ** - * Checks if the element has a given class name in the list of class names applied to it. - - value (string) class name - ** - = (boolean) `true` if the element has given class - \*/ - elproto.hasClass = function (value) { - var elem = this.node, - className = elem.className.baseVal, - curClasses = className.match(rgNotSpace) || []; - return !!~curClasses.indexOf(value); - }; - /*\ - * Element.toggleClass - [ method ] - ** - * Add or remove one or more classes from the element, depending on either - * the class’s presence or the value of the `flag` argument. - - value (string) class name or space separated list of class names - - flag (boolean) value to determine whether the class should be added or removed - ** - = (Element) original element. - \*/ - elproto.toggleClass = function (value, flag) { - if (flag != null) { - if (flag) { - return this.addClass(value); - } else { - return this.removeClass(value); - } - } - var classes = (value || "").match(rgNotSpace) || [], - elem = this.node, - className = elem.className.baseVal, - curClasses = className.match(rgNotSpace) || [], - j, - pos, - clazz, - finalValue; - j = 0; - while ((clazz = classes[j++])) { - pos = curClasses.indexOf(clazz); - if (~pos) { - curClasses.splice(pos, 1); - } else { - curClasses.push(clazz); - } - } - - finalValue = curClasses.join(" "); - if (className != finalValue) { - elem.className.baseVal = finalValue; - } - return this; - }; -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var operators = { - "+": function (x, y) { - return x + y; - }, - "-": function (x, y) { - return x - y; - }, - "/": function (x, y) { - return x / y; - }, - "*": function (x, y) { - return x * y; - } - }, - Str = String, - reUnit = /[a-z]+$/i, - reAddon = /^\s*([+\-\/*])\s*=\s*([\d.eE+\-]+)\s*([^\d\s]+)?\s*$/; - function getNumber(val) { - return val; - } - function getUnit(unit) { - return function (val) { - return +val.toFixed(3) + unit; - }; - } - eve.on("snap.util.attr", function (val) { - var plus = Str(val).match(reAddon); - if (plus) { - var evnt = eve.nt(), - name = evnt.substring(evnt.lastIndexOf(".") + 1), - a = this.attr(name), - atr = {}; - eve.stop(); - var unit = plus[3] || "", - aUnit = a.match(reUnit), - op = operators[plus[1]]; - if (aUnit && aUnit == unit) { - val = op(parseFloat(a), +plus[2]); - } else { - a = this.asPX(name); - val = op(this.asPX(name), this.asPX(name, plus[2] + unit)); - } - if (isNaN(a) || isNaN(val)) { - return; - } - atr[name] = val; - this.attr(atr); - } - })(-10); - eve.on("snap.util.equal", function (name, b) { - var A, B, a = Str(this.attr(name) || ""), - el = this, - bplus = Str(b).match(reAddon); - if (bplus) { - eve.stop(); - var unit = bplus[3] || "", - aUnit = a.match(reUnit), - op = operators[bplus[1]]; - if (aUnit && aUnit == unit) { - return { - from: parseFloat(a), - to: op(parseFloat(a), +bplus[2]), - f: getUnit(aUnit) - }; - } else { - a = this.asPX(name); - return { - from: a, - to: op(a, this.asPX(name, bplus[2] + unit)), - f: getNumber - }; - } - } - })(-10); -}); -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var proto = Paper.prototype, - is = Snap.is; - /*\ - * Paper.rect - [ method ] - * - * Draws a rectangle - ** - - x (number) x coordinate of the top left corner - - y (number) y coordinate of the top left corner - - width (number) width - - height (number) height - - rx (number) #optional horizontal radius for rounded corners, default is 0 - - ry (number) #optional vertical radius for rounded corners, default is rx or 0 - = (object) the `rect` element - ** - > Usage - | // regular rectangle - | var c = paper.rect(10, 10, 50, 50); - | // rectangle with rounded corners - | var c = paper.rect(40, 40, 50, 50, 10); - \*/ - proto.rect = function (x, y, w, h, rx, ry) { - var attr; - if (ry == null) { - ry = rx; - } - if (is(x, "object") && x == "[object Object]") { - attr = x; - } else if (x != null) { - attr = { - x: x, - y: y, - width: w, - height: h - }; - if (rx != null) { - attr.rx = rx; - attr.ry = ry; - } - } - return this.el("rect", attr); - }; - /*\ - * Paper.circle - [ method ] - ** - * Draws a circle - ** - - x (number) x coordinate of the centre - - y (number) y coordinate of the centre - - r (number) radius - = (object) the `circle` element - ** - > Usage - | var c = paper.circle(50, 50, 40); - \*/ - proto.circle = function (cx, cy, r) { - var attr; - if (is(cx, "object") && cx == "[object Object]") { - attr = cx; - } else if (cx != null) { - attr = { - cx: cx, - cy: cy, - r: r - }; - } - return this.el("circle", attr); - }; - - var preload = (function () { - function onerror() { - this.parentNode.removeChild(this); - } - return function (src, f) { - var img = glob.doc.createElement("img"), - body = glob.doc.body; - img.style.cssText = "position:absolute;left:-9999em;top:-9999em"; - img.onload = function () { - f.call(img); - img.onload = img.onerror = null; - body.removeChild(img); - }; - img.onerror = onerror; - body.appendChild(img); - img.src = src; - }; - }()); - - /*\ - * Paper.image - [ method ] - ** - * Places an image on the surface - ** - - src (string) URI of the source image - - x (number) x offset position - - y (number) y offset position - - width (number) width of the image - - height (number) height of the image - = (object) the `image` element - * or - = (object) Snap element object with type `image` - ** - > Usage - | var c = paper.image("apple.png", 10, 10, 80, 80); - \*/ - proto.image = function (src, x, y, width, height) { - var el = this.el("image"); - if (is(src, "object") && "src" in src) { - el.attr(src); - } else if (src != null) { - var set = { - "xlink:href": src, - preserveAspectRatio: "none" - }; - if (x != null && y != null) { - set.x = x; - set.y = y; - } - if (width != null && height != null) { - set.width = width; - set.height = height; - } else { - preload(src, function () { - Snap._.$(el.node, { - width: this.offsetWidth, - height: this.offsetHeight - }); - }); - } - Snap._.$(el.node, set); - } - return el; - }; - /*\ - * Paper.ellipse - [ method ] - ** - * Draws an ellipse - ** - - x (number) x coordinate of the centre - - y (number) y coordinate of the centre - - rx (number) horizontal radius - - ry (number) vertical radius - = (object) the `ellipse` element - ** - > Usage - | var c = paper.ellipse(50, 50, 40, 20); - \*/ - proto.ellipse = function (cx, cy, rx, ry) { - var attr; - if (is(cx, "object") && cx == "[object Object]") { - attr = cx; - } else if (cx != null) { - attr ={ - cx: cx, - cy: cy, - rx: rx, - ry: ry - }; - } - return this.el("ellipse", attr); - }; - // SIERRA Paper.path(): Unclear from the link what a Catmull-Rom curveto is, and why it would make life any easier. - /*\ - * Paper.path - [ method ] - ** - * Creates a `` element using the given string as the path's definition - - pathString (string) #optional path string in SVG format - * Path string consists of one-letter commands, followed by comma seprarated arguments in numerical form. Example: - | "M10,20L30,40" - * This example features two commands: `M`, with arguments `(10, 20)` and `L` with arguments `(30, 40)`. Uppercase letter commands express coordinates in absolute terms, while lowercase commands express them in relative terms from the most recently declared coordinates. - * - #

Here is short list of commands available, for more details see SVG path string format or article about path strings at MDN.

- # - # - # - # - # - # - # - # - # - # - # - #
CommandNameParameters
Mmoveto(x y)+
Zclosepath(none)
Llineto(x y)+
Hhorizontal linetox+
Vvertical linetoy+
Ccurveto(x1 y1 x2 y2 x y)+
Ssmooth curveto(x2 y2 x y)+
Qquadratic Bézier curveto(x1 y1 x y)+
Tsmooth quadratic Bézier curveto(x y)+
Aelliptical arc(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
RCatmull-Rom curveto*x1 y1 (x y)+
- * * _Catmull-Rom curveto_ is a not standard SVG command and added to make life easier. - * Note: there is a special case when a path consists of only three commands: `M10,10R…z`. In this case the path connects back to its starting point. - > Usage - | var c = paper.path("M10 10L90 90"); - | // draw a diagonal line: - | // move to 10,10, line to 90,90 - \*/ - proto.path = function (d) { - var attr; - if (is(d, "object") && !is(d, "array")) { - attr = d; - } else if (d) { - attr = {d: d}; - } - return this.el("path", attr); - }; - /*\ - * Paper.g - [ method ] - ** - * Creates a group element - ** - - varargs (…) #optional elements to nest within the group - = (object) the `g` element - ** - > Usage - | var c1 = paper.circle(), - | c2 = paper.rect(), - | g = paper.g(c2, c1); // note that the order of elements is different - * or - | var c1 = paper.circle(), - | c2 = paper.rect(), - | g = paper.g(); - | g.add(c2, c1); - \*/ - /*\ - * Paper.group - [ method ] - ** - * See @Paper.g - \*/ - proto.group = proto.g = function (first) { - var attr, - el = this.el("g"); - if (arguments.length == 1 && first && !first.type) { - el.attr(first); - } else if (arguments.length) { - el.add(Array.prototype.slice.call(arguments, 0)); - } - return el; - }; - /*\ - * Paper.svg - [ method ] - ** - * Creates a nested SVG element. - - x (number) @optional X of the element - - y (number) @optional Y of the element - - width (number) @optional width of the element - - height (number) @optional height of the element - - vbx (number) @optional viewbox X - - vby (number) @optional viewbox Y - - vbw (number) @optional viewbox width - - vbh (number) @optional viewbox height - ** - = (object) the `svg` element - ** - \*/ - proto.svg = function (x, y, width, height, vbx, vby, vbw, vbh) { - var attrs = {}; - if (is(x, "object") && y == null) { - attrs = x; - } else { - if (x != null) { - attrs.x = x; - } - if (y != null) { - attrs.y = y; - } - if (width != null) { - attrs.width = width; - } - if (height != null) { - attrs.height = height; - } - if (vbx != null && vby != null && vbw != null && vbh != null) { - attrs.viewBox = [vbx, vby, vbw, vbh]; - } - } - return this.el("svg", attrs); - }; - /*\ - * Paper.mask - [ method ] - ** - * Equivalent in behaviour to @Paper.g, except it’s a mask. - ** - = (object) the `mask` element - ** - \*/ - proto.mask = function (first) { - var attr, - el = this.el("mask"); - if (arguments.length == 1 && first && !first.type) { - el.attr(first); - } else if (arguments.length) { - el.add(Array.prototype.slice.call(arguments, 0)); - } - return el; - }; - /*\ - * Paper.ptrn - [ method ] - ** - * Equivalent in behaviour to @Paper.g, except it’s a pattern. - - x (number) @optional X of the element - - y (number) @optional Y of the element - - width (number) @optional width of the element - - height (number) @optional height of the element - - vbx (number) @optional viewbox X - - vby (number) @optional viewbox Y - - vbw (number) @optional viewbox width - - vbh (number) @optional viewbox height - ** - = (object) the `pattern` element - ** - \*/ - proto.ptrn = function (x, y, width, height, vx, vy, vw, vh) { - if (is(x, "object")) { - var attr = x; - } else { - attr = {patternUnits: "userSpaceOnUse"}; - if (x) { - attr.x = x; - } - if (y) { - attr.y = y; - } - if (width != null) { - attr.width = width; - } - if (height != null) { - attr.height = height; - } - if (vx != null && vy != null && vw != null && vh != null) { - attr.viewBox = [vx, vy, vw, vh]; - } else { - attr.viewBox = [x || 0, y || 0, width || 0, height || 0]; - } - } - return this.el("pattern", attr); - }; - /*\ - * Paper.use - [ method ] - ** - * Creates a element. - - id (string) @optional id of element to link - * or - - id (Element) @optional element to link - ** - = (object) the `use` element - ** - \*/ - proto.use = function (id) { - if (id != null) { - if (id instanceof Element) { - if (!id.attr("id")) { - id.attr({id: Snap._.id(id)}); - } - id = id.attr("id"); - } - if (String(id).charAt() == "#") { - id = id.substring(1); - } - return this.el("use", {"xlink:href": "#" + id}); - } else { - return Element.prototype.use.call(this); - } - }; - /*\ - * Paper.symbol - [ method ] - ** - * Creates a element. - - vbx (number) @optional viewbox X - - vby (number) @optional viewbox Y - - vbw (number) @optional viewbox width - - vbh (number) @optional viewbox height - = (object) the `symbol` element - ** - \*/ - proto.symbol = function (vx, vy, vw, vh) { - var attr = {}; - if (vx != null && vy != null && vw != null && vh != null) { - attr.viewBox = [vx, vy, vw, vh]; - } - - return this.el("symbol", attr); - }; - /*\ - * Paper.text - [ method ] - ** - * Draws a text string - ** - - x (number) x coordinate position - - y (number) y coordinate position - - text (string|array) The text string to draw or array of strings to nest within separate `` elements - = (object) the `text` element - ** - > Usage - | var t1 = paper.text(50, 50, "Snap"); - | var t2 = paper.text(50, 50, ["S","n","a","p"]); - | // Text path usage - | t1.attr({textpath: "M10,10L100,100"}); - | // or - | var pth = paper.path("M10,10L100,100"); - | t1.attr({textpath: pth}); - \*/ - proto.text = function (x, y, text) { - var attr = {}; - if (is(x, "object")) { - attr = x; - } else if (x != null) { - attr = { - x: x, - y: y, - text: text || "" - }; - } - return this.el("text", attr); - }; - /*\ - * Paper.line - [ method ] - ** - * Draws a line - ** - - x1 (number) x coordinate position of the start - - y1 (number) y coordinate position of the start - - x2 (number) x coordinate position of the end - - y2 (number) y coordinate position of the end - = (object) the `line` element - ** - > Usage - | var t1 = paper.line(50, 50, 100, 100); - \*/ - proto.line = function (x1, y1, x2, y2) { - var attr = {}; - if (is(x1, "object")) { - attr = x1; - } else if (x1 != null) { - attr = { - x1: x1, - x2: x2, - y1: y1, - y2: y2 - }; - } - return this.el("line", attr); - }; - /*\ - * Paper.polyline - [ method ] - ** - * Draws a polyline - ** - - points (array) array of points - * or - - varargs (…) points - = (object) the `polyline` element - ** - > Usage - | var p1 = paper.polyline([10, 10, 100, 100]); - | var p2 = paper.polyline(10, 10, 100, 100); - \*/ - proto.polyline = function (points) { - if (arguments.length > 1) { - points = Array.prototype.slice.call(arguments, 0); - } - var attr = {}; - if (is(points, "object") && !is(points, "array")) { - attr = points; - } else if (points != null) { - attr = {points: points}; - } - return this.el("polyline", attr); - }; - /*\ - * Paper.polygon - [ method ] - ** - * Draws a polygon. See @Paper.polyline - \*/ - proto.polygon = function (points) { - if (arguments.length > 1) { - points = Array.prototype.slice.call(arguments, 0); - } - var attr = {}; - if (is(points, "object") && !is(points, "array")) { - attr = points; - } else if (points != null) { - attr = {points: points}; - } - return this.el("polygon", attr); - }; - // gradients - (function () { - var $ = Snap._.$; - // gradients' helpers - function Gstops() { - return this.selectAll("stop"); - } - function GaddStop(color, offset) { - var stop = $("stop"), - attr = { - offset: +offset + "%" - }; - color = Snap.color(color); - attr["stop-color"] = color.hex; - if (color.opacity < 1) { - attr["stop-opacity"] = color.opacity; - } - $(stop, attr); - this.node.appendChild(stop); - return this; - } - function GgetBBox() { - if (this.type == "linearGradient") { - var x1 = $(this.node, "x1") || 0, - x2 = $(this.node, "x2") || 1, - y1 = $(this.node, "y1") || 0, - y2 = $(this.node, "y2") || 0; - return Snap._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1)); - } else { - var cx = this.node.cx || .5, - cy = this.node.cy || .5, - r = this.node.r || 0; - return Snap._.box(cx - r, cy - r, r * 2, r * 2); - } - } - function gradient(defs, str) { - var grad = eve("snap.util.grad.parse", null, str).firstDefined(), - el; - if (!grad) { - return null; - } - grad.params.unshift(defs); - if (grad.type.toLowerCase() == "l") { - el = gradientLinear.apply(0, grad.params); - } else { - el = gradientRadial.apply(0, grad.params); - } - if (grad.type != grad.type.toLowerCase()) { - $(el.node, { - gradientUnits: "userSpaceOnUse" - }); - } - var stops = grad.stops, - len = stops.length, - start = 0, - j = 0; - function seed(i, end) { - var step = (end - start) / (i - j); - for (var k = j; k < i; k++) { - stops[k].offset = +(+start + step * (k - j)).toFixed(2); - } - j = i; - start = end; - } - len--; - for (var i = 0; i < len; i++) if ("offset" in stops[i]) { - seed(i, stops[i].offset); - } - stops[len].offset = stops[len].offset || 100; - seed(len, stops[len].offset); - for (i = 0; i <= len; i++) { - var stop = stops[i]; - el.addStop(stop.color, stop.offset); - } - return el; - } - function gradientLinear(defs, x1, y1, x2, y2) { - var el = Snap._.make("linearGradient", defs); - el.stops = Gstops; - el.addStop = GaddStop; - el.getBBox = GgetBBox; - if (x1 != null) { - $(el.node, { - x1: x1, - y1: y1, - x2: x2, - y2: y2 - }); - } - return el; - } - function gradientRadial(defs, cx, cy, r, fx, fy) { - var el = Snap._.make("radialGradient", defs); - el.stops = Gstops; - el.addStop = GaddStop; - el.getBBox = GgetBBox; - if (cx != null) { - $(el.node, { - cx: cx, - cy: cy, - r: r - }); - } - if (fx != null && fy != null) { - $(el.node, { - fx: fx, - fy: fy - }); - } - return el; - } - /*\ - * Paper.gradient - [ method ] - ** - * Creates a gradient element - ** - - gradient (string) gradient descriptor - > Gradient Descriptor - * The gradient descriptor is an expression formatted as - * follows: `()`. The `` can be - * either linear or radial. The uppercase `L` or `R` letters - * indicate absolute coordinates offset from the SVG surface. - * Lowercase `l` or `r` letters indicate coordinates - * calculated relative to the element to which the gradient is - * applied. Coordinates specify a linear gradient vector as - * `x1`, `y1`, `x2`, `y2`, or a radial gradient as `cx`, `cy`, - * `r` and optional `fx`, `fy` specifying a focal point away - * from the center of the circle. Specify `` as a list - * of dash-separated CSS color values. Each color may be - * followed by a custom offset value, separated with a colon - * character. - > Examples - * Linear gradient, relative from top-left corner to bottom-right - * corner, from black through red to white: - | var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff"); - * Linear gradient, absolute from (0, 0) to (100, 100), from black - * through red at 25% to white: - | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25-#fff"); - * Radial gradient, relative from the center of the element with radius - * half the width, from black to white: - | var g = paper.gradient("r(0.5, 0.5, 0.5)#000-#fff"); - * To apply the gradient: - | paper.circle(50, 50, 40).attr({ - | fill: g - | }); - = (object) the `gradient` element - \*/ - proto.gradient = function (str) { - return gradient(this.defs, str); - }; - proto.gradientLinear = function (x1, y1, x2, y2) { - return gradientLinear(this.defs, x1, y1, x2, y2); - }; - proto.gradientRadial = function (cx, cy, r, fx, fy) { - return gradientRadial(this.defs, cx, cy, r, fx, fy); - }; - /*\ - * Paper.toString - [ method ] - ** - * Returns SVG code for the @Paper - = (string) SVG code for the @Paper - \*/ - proto.toString = function () { - var doc = this.node.ownerDocument, - f = doc.createDocumentFragment(), - d = doc.createElement("div"), - svg = this.node.cloneNode(true), - res; - f.appendChild(d); - d.appendChild(svg); - Snap._.$(svg, {xmlns: "http://www.w3.org/2000/svg"}); - res = d.innerHTML; - f.removeChild(f.firstChild); - return res; - }; - /*\ - * Paper.toDataURL - [ method ] - ** - * Returns SVG code for the @Paper as Data URI string. - = (string) Data URI string - \*/ - proto.toDataURL = function () { - if (window && window.btoa) { - return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(this))); - } - }; - /*\ - * Paper.clear - [ method ] - ** - * Removes all child nodes of the paper, except . - \*/ - proto.clear = function () { - var node = this.node.firstChild, - next; - while (node) { - next = node.nextSibling; - if (node.tagName != "defs") { - node.parentNode.removeChild(node); - } else { - proto.clear.call({node: node}); - } - node = next; - } - }; - }()); -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob) { - var elproto = Element.prototype, - is = Snap.is, - clone = Snap._.clone, - has = "hasOwnProperty", - p2s = /,?([a-z]),?/gi, - toFloat = parseFloat, - math = Math, - PI = math.PI, - mmin = math.min, - mmax = math.max, - pow = math.pow, - abs = math.abs; - function paths(ps) { - var p = paths.ps = paths.ps || {}; - if (p[ps]) { - p[ps].sleep = 100; - } else { - p[ps] = { - sleep: 100 - }; - } - setTimeout(function () { - for (var key in p) if (p[has](key) && key != ps) { - p[key].sleep--; - !p[key].sleep && delete p[key]; - } - }); - return p[ps]; - } - function box(x, y, width, height) { - if (x == null) { - x = y = width = height = 0; - } - if (y == null) { - y = x.y; - width = x.width; - height = x.height; - x = x.x; - } - return { - x: x, - y: y, - width: width, - w: width, - height: height, - h: height, - x2: x + width, - y2: y + height, - cx: x + width / 2, - cy: y + height / 2, - r1: math.min(width, height) / 2, - r2: math.max(width, height) / 2, - r0: math.sqrt(width * width + height * height) / 2, - path: rectPath(x, y, width, height), - vb: [x, y, width, height].join(" ") - }; - } - function toString() { - return this.join(",").replace(p2s, "$1"); - } - function pathClone(pathArray) { - var res = clone(pathArray); - res.toString = toString; - return res; - } - function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { - if (length == null) { - return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); - } else { - return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, - getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length)); - } - } - function getLengthFactory(istotal, subpath) { - function O(val) { - return +(+val).toFixed(3); - } - return Snap._.cacher(function (path, length, onlystart) { - if (path instanceof Element) { - path = path.attr("d"); - } - path = path2curve(path); - var x, y, p, l, sp = "", subpaths = {}, point, - len = 0; - for (var i = 0, ii = path.length; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = +p[1]; - y = +p[2]; - } else { - l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - if (len + l > length) { - if (subpath && !subpaths.start) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - sp += [ - "C" + O(point.start.x), - O(point.start.y), - O(point.m.x), - O(point.m.y), - O(point.x), - O(point.y) - ]; - if (onlystart) {return sp;} - subpaths.start = sp; - sp = [ - "M" + O(point.x), - O(point.y) + "C" + O(point.n.x), - O(point.n.y), - O(point.end.x), - O(point.end.y), - O(p[5]), - O(p[6]) - ].join(); - len += l; - x = +p[5]; - y = +p[6]; - continue; - } - if (!istotal && !subpath) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - return point; - } - } - len += l; - x = +p[5]; - y = +p[6]; - } - sp += p.shift() + p; - } - subpaths.end = sp; - point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); - return point; - }, null, Snap._.clone); - } - var getTotalLength = getLengthFactory(1), - getPointAtLength = getLengthFactory(), - getSubpathsAtLength = getLengthFactory(0, 1); - function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t, - t13 = pow(t1, 3), - t12 = pow(t1, 2), - t2 = t * t, - t3 = t2 * t, - x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, - y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, - mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), - my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), - nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), - ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), - ax = t1 * p1x + t * c1x, - ay = t1 * p1y + t * c1y, - cx = t1 * c2x + t * p2x, - cy = t1 * c2y + t * p2y, - alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI); - // (mx > nx || my < ny) && (alpha += 180); - return { - x: x, - y: y, - m: {x: mx, y: my}, - n: {x: nx, y: ny}, - start: {x: ax, y: ay}, - end: {x: cx, y: cy}, - alpha: alpha - }; - } - function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - if (!Snap.is(p1x, "array")) { - p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y]; - } - var bbox = curveDim.apply(null, p1x); - return box( - bbox.min.x, - bbox.min.y, - bbox.max.x - bbox.min.x, - bbox.max.y - bbox.min.y - ); - } - function isPointInsideBBox(bbox, x, y) { - return x >= bbox.x && - x <= bbox.x + bbox.width && - y >= bbox.y && - y <= bbox.y + bbox.height; - } - function isBBoxIntersect(bbox1, bbox2) { - bbox1 = box(bbox1); - bbox2 = box(bbox2); - return isPointInsideBBox(bbox2, bbox1.x, bbox1.y) - || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y) - || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2) - || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2) - || isPointInsideBBox(bbox1, bbox2.x, bbox2.y) - || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y) - || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2) - || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2) - || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x - || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) - && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y - || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); - } - function base3(t, p1, p2, p3, p4) { - var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, - t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; - return t * t2 - 3 * p1 + 3 * p2; - } - function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { - if (z == null) { - z = 1; - } - z = z > 1 ? 1 : z < 0 ? 0 : z; - var z2 = z / 2, - n = 12, - Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], - Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472], - sum = 0; - for (var i = 0; i < n; i++) { - var ct = z2 * Tvalues[i] + z2, - xbase = base3(ct, x1, x2, x3, x4), - ybase = base3(ct, y1, y2, y3, y4), - comb = xbase * xbase + ybase * ybase; - sum += Cvalues[i] * math.sqrt(comb); - } - return z2 * sum; - } - function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) { - if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) { - return; - } - var t = 1, - step = t / 2, - t2 = t - step, - l, - e = .01; - l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); - while (abs(l - ll) > e) { - step /= 2; - t2 += (l < ll ? 1 : -1) * step; - l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); - } - return t2; - } - function intersect(x1, y1, x2, y2, x3, y3, x4, y4) { - if ( - mmax(x1, x2) < mmin(x3, x4) || - mmin(x1, x2) > mmax(x3, x4) || - mmax(y1, y2) < mmin(y3, y4) || - mmin(y1, y2) > mmax(y3, y4) - ) { - return; - } - var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), - ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), - denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if (!denominator) { - return; - } - var px = nx / denominator, - py = ny / denominator, - px2 = +px.toFixed(2), - py2 = +py.toFixed(2); - if ( - px2 < +mmin(x1, x2).toFixed(2) || - px2 > +mmax(x1, x2).toFixed(2) || - px2 < +mmin(x3, x4).toFixed(2) || - px2 > +mmax(x3, x4).toFixed(2) || - py2 < +mmin(y1, y2).toFixed(2) || - py2 > +mmax(y1, y2).toFixed(2) || - py2 < +mmin(y3, y4).toFixed(2) || - py2 > +mmax(y3, y4).toFixed(2) - ) { - return; - } - return {x: px, y: py}; - } - function inter(bez1, bez2) { - return interHelper(bez1, bez2); - } - function interCount(bez1, bez2) { - return interHelper(bez1, bez2, 1); - } - function interHelper(bez1, bez2, justCount) { - var bbox1 = bezierBBox(bez1), - bbox2 = bezierBBox(bez2); - if (!isBBoxIntersect(bbox1, bbox2)) { - return justCount ? 0 : []; - } - var l1 = bezlen.apply(0, bez1), - l2 = bezlen.apply(0, bez2), - n1 = ~~(l1 / 8), - n2 = ~~(l2 / 8), - dots1 = [], - dots2 = [], - xy = {}, - res = justCount ? 0 : []; - for (var i = 0; i < n1 + 1; i++) { - var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); - dots1.push({x: p.x, y: p.y, t: i / n1}); - } - for (i = 0; i < n2 + 1; i++) { - p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); - dots2.push({x: p.x, y: p.y, t: i / n2}); - } - for (i = 0; i < n1; i++) { - for (var j = 0; j < n2; j++) { - var di = dots1[i], - di1 = dots1[i + 1], - dj = dots2[j], - dj1 = dots2[j + 1], - ci = abs(di1.x - di.x) < .001 ? "y" : "x", - cj = abs(dj1.x - dj.x) < .001 ? "y" : "x", - is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y); - if (is) { - if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) { - continue; - } - xy[is.x.toFixed(4)] = is.y.toFixed(4); - var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), - t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); - if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { - if (justCount) { - res++; - } else { - res.push({ - x: is.x, - y: is.y, - t1: t1, - t2: t2 - }); - } - } - } - } - } - return res; - } - function pathIntersection(path1, path2) { - return interPathHelper(path1, path2); - } - function pathIntersectionNumber(path1, path2) { - return interPathHelper(path1, path2, 1); - } - function interPathHelper(path1, path2, justCount) { - path1 = path2curve(path1); - path2 = path2curve(path2); - var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, - res = justCount ? 0 : []; - for (var i = 0, ii = path1.length; i < ii; i++) { - var pi = path1[i]; - if (pi[0] == "M") { - x1 = x1m = pi[1]; - y1 = y1m = pi[2]; - } else { - if (pi[0] == "C") { - bez1 = [x1, y1].concat(pi.slice(1)); - x1 = bez1[6]; - y1 = bez1[7]; - } else { - bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; - x1 = x1m; - y1 = y1m; - } - for (var j = 0, jj = path2.length; j < jj; j++) { - var pj = path2[j]; - if (pj[0] == "M") { - x2 = x2m = pj[1]; - y2 = y2m = pj[2]; - } else { - if (pj[0] == "C") { - bez2 = [x2, y2].concat(pj.slice(1)); - x2 = bez2[6]; - y2 = bez2[7]; - } else { - bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; - x2 = x2m; - y2 = y2m; - } - var intr = interHelper(bez1, bez2, justCount); - if (justCount) { - res += intr; - } else { - for (var k = 0, kk = intr.length; k < kk; k++) { - intr[k].segment1 = i; - intr[k].segment2 = j; - intr[k].bez1 = bez1; - intr[k].bez2 = bez2; - } - res = res.concat(intr); - } - } - } - } - } - return res; - } - function isPointInsidePath(path, x, y) { - var bbox = pathBBox(path); - return isPointInsideBBox(bbox, x, y) && - interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1; - } - function pathBBox(path) { - var pth = paths(path); - if (pth.bbox) { - return clone(pth.bbox); - } - if (!path) { - return box(); - } - path = path2curve(path); - var x = 0, - y = 0, - X = [], - Y = [], - p; - for (var i = 0, ii = path.length; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = p[1]; - y = p[2]; - X.push(x); - Y.push(y); - } else { - var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - X = X.concat(dim.min.x, dim.max.x); - Y = Y.concat(dim.min.y, dim.max.y); - x = p[5]; - y = p[6]; - } - } - var xmin = mmin.apply(0, X), - ymin = mmin.apply(0, Y), - xmax = mmax.apply(0, X), - ymax = mmax.apply(0, Y), - bb = box(xmin, ymin, xmax - xmin, ymax - ymin); - pth.bbox = clone(bb); - return bb; - } - function rectPath(x, y, w, h, r) { - if (r) { - return [ - ["M", +x + (+r), y], - ["l", w - r * 2, 0], - ["a", r, r, 0, 0, 1, r, r], - ["l", 0, h - r * 2], - ["a", r, r, 0, 0, 1, -r, r], - ["l", r * 2 - w, 0], - ["a", r, r, 0, 0, 1, -r, -r], - ["l", 0, r * 2 - h], - ["a", r, r, 0, 0, 1, r, -r], - ["z"] - ]; - } - var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; - res.toString = toString; - return res; - } - function ellipsePath(x, y, rx, ry, a) { - if (a == null && ry == null) { - ry = rx; - } - x = +x; - y = +y; - rx = +rx; - ry = +ry; - if (a != null) { - var rad = Math.PI / 180, - x1 = x + rx * Math.cos(-ry * rad), - x2 = x + rx * Math.cos(-a * rad), - y1 = y + rx * Math.sin(-ry * rad), - y2 = y + rx * Math.sin(-a * rad), - res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]]; - } else { - res = [ - ["M", x, y], - ["m", 0, -ry], - ["a", rx, ry, 0, 1, 1, 0, 2 * ry], - ["a", rx, ry, 0, 1, 1, 0, -2 * ry], - ["z"] - ]; - } - res.toString = toString; - return res; - } - var unit2px = Snap._unit2px, - getPath = { - path: function (el) { - return el.attr("path"); - }, - circle: function (el) { - var attr = unit2px(el); - return ellipsePath(attr.cx, attr.cy, attr.r); - }, - ellipse: function (el) { - var attr = unit2px(el); - return ellipsePath(attr.cx || 0, attr.cy || 0, attr.rx, attr.ry); - }, - rect: function (el) { - var attr = unit2px(el); - return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height, attr.rx, attr.ry); - }, - image: function (el) { - var attr = unit2px(el); - return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height); - }, - line: function (el) { - return "M" + [el.attr("x1") || 0, el.attr("y1") || 0, el.attr("x2"), el.attr("y2")]; - }, - polyline: function (el) { - return "M" + el.attr("points"); - }, - polygon: function (el) { - return "M" + el.attr("points") + "z"; - }, - deflt: function (el) { - var bbox = el.node.getBBox(); - return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); - } - }; - function pathToRelative(pathArray) { - var pth = paths(pathArray), - lowerCase = String.prototype.toLowerCase; - if (pth.rel) { - return pathClone(pth.rel); - } - if (!Snap.is(pathArray, "array") || !Snap.is(pathArray && pathArray[0], "array")) { - pathArray = Snap.parsePathString(pathArray); - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0; - if (pathArray[0][0] == "M") { - x = pathArray[0][1]; - y = pathArray[0][2]; - mx = x; - my = y; - start++; - res.push(["M", x, y]); - } - for (var i = start, ii = pathArray.length; i < ii; i++) { - var r = res[i] = [], - pa = pathArray[i]; - if (pa[0] != lowerCase.call(pa[0])) { - r[0] = lowerCase.call(pa[0]); - switch (r[0]) { - case "a": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +(pa[6] - x).toFixed(3); - r[7] = +(pa[7] - y).toFixed(3); - break; - case "v": - r[1] = +(pa[1] - y).toFixed(3); - break; - case "m": - mx = pa[1]; - my = pa[2]; - default: - for (var j = 1, jj = pa.length; j < jj; j++) { - r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); - } - } - } else { - r = res[i] = []; - if (pa[0] == "m") { - mx = pa[1] + x; - my = pa[2] + y; - } - for (var k = 0, kk = pa.length; k < kk; k++) { - res[i][k] = pa[k]; - } - } - var len = res[i].length; - switch (res[i][0]) { - case "z": - x = mx; - y = my; - break; - case "h": - x += +res[i][len - 1]; - break; - case "v": - y += +res[i][len - 1]; - break; - default: - x += +res[i][len - 2]; - y += +res[i][len - 1]; - } - } - res.toString = toString; - pth.rel = pathClone(res); - return res; - } - function pathToAbsolute(pathArray) { - var pth = paths(pathArray); - if (pth.abs) { - return pathClone(pth.abs); - } - if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = Snap.parsePathString(pathArray); - } - if (!pathArray || !pathArray.length) { - return [["M", 0, 0]]; - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0, - pa0; - if (pathArray[0][0] == "M") { - x = +pathArray[0][1]; - y = +pathArray[0][2]; - mx = x; - my = y; - start++; - res[0] = ["M", x, y]; - } - var crz = pathArray.length == 3 && - pathArray[0][0] == "M" && - pathArray[1][0].toUpperCase() == "R" && - pathArray[2][0].toUpperCase() == "Z"; - for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { - res.push(r = []); - pa = pathArray[i]; - pa0 = pa[0]; - if (pa0 != pa0.toUpperCase()) { - r[0] = pa0.toUpperCase(); - switch (r[0]) { - case "A": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +pa[6] + x; - r[7] = +pa[7] + y; - break; - case "V": - r[1] = +pa[1] + y; - break; - case "H": - r[1] = +pa[1] + x; - break; - case "R": - var dots = [x, y].concat(pa.slice(1)); - for (var j = 2, jj = dots.length; j < jj; j++) { - dots[j] = +dots[j] + x; - dots[++j] = +dots[j] + y; - } - res.pop(); - res = res.concat(catmullRom2bezier(dots, crz)); - break; - case "O": - res.pop(); - dots = ellipsePath(x, y, pa[1], pa[2]); - dots.push(dots[0]); - res = res.concat(dots); - break; - case "U": - res.pop(); - res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); - r = ["U"].concat(res[res.length - 1].slice(-2)); - break; - case "M": - mx = +pa[1] + x; - my = +pa[2] + y; - default: - for (j = 1, jj = pa.length; j < jj; j++) { - r[j] = +pa[j] + ((j % 2) ? x : y); - } - } - } else if (pa0 == "R") { - dots = [x, y].concat(pa.slice(1)); - res.pop(); - res = res.concat(catmullRom2bezier(dots, crz)); - r = ["R"].concat(pa.slice(-2)); - } else if (pa0 == "O") { - res.pop(); - dots = ellipsePath(x, y, pa[1], pa[2]); - dots.push(dots[0]); - res = res.concat(dots); - } else if (pa0 == "U") { - res.pop(); - res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); - r = ["U"].concat(res[res.length - 1].slice(-2)); - } else { - for (var k = 0, kk = pa.length; k < kk; k++) { - r[k] = pa[k]; - } - } - pa0 = pa0.toUpperCase(); - if (pa0 != "O") { - switch (r[0]) { - case "Z": - x = +mx; - y = +my; - break; - case "H": - x = r[1]; - break; - case "V": - y = r[1]; - break; - case "M": - mx = r[r.length - 2]; - my = r[r.length - 1]; - default: - x = r[r.length - 2]; - y = r[r.length - 1]; - } - } - } - res.toString = toString; - pth.abs = pathClone(res); - return res; - } - function l2c(x1, y1, x2, y2) { - return [x1, y1, x2, y2, x2, y2]; - } - function q2c(x1, y1, ax, ay, x2, y2) { - var _13 = 1 / 3, - _23 = 2 / 3; - return [ - _13 * x1 + _23 * ax, - _13 * y1 + _23 * ay, - _13 * x2 + _23 * ax, - _13 * y2 + _23 * ay, - x2, - y2 - ]; - } - function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { - // for more information of where this math came from visit: - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - var _120 = PI * 120 / 180, - rad = PI / 180 * (+angle || 0), - res = [], - xy, - rotate = Snap._.cacher(function (x, y, rad) { - var X = x * math.cos(rad) - y * math.sin(rad), - Y = x * math.sin(rad) + y * math.cos(rad); - return {x: X, y: Y}; - }); - if (!recursive) { - xy = rotate(x1, y1, -rad); - x1 = xy.x; - y1 = xy.y; - xy = rotate(x2, y2, -rad); - x2 = xy.x; - y2 = xy.y; - var cos = math.cos(PI / 180 * angle), - sin = math.sin(PI / 180 * angle), - x = (x1 - x2) / 2, - y = (y1 - y2) / 2; - var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); - if (h > 1) { - h = math.sqrt(h); - rx = h * rx; - ry = h * ry; - } - var rx2 = rx * rx, - ry2 = ry * ry, - k = (large_arc_flag == sweep_flag ? -1 : 1) * - math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), - cx = k * rx * y / ry + (x1 + x2) / 2, - cy = k * -ry * x / rx + (y1 + y2) / 2, - f1 = math.asin(((y1 - cy) / ry).toFixed(9)), - f2 = math.asin(((y2 - cy) / ry).toFixed(9)); - - f1 = x1 < cx ? PI - f1 : f1; - f2 = x2 < cx ? PI - f2 : f2; - f1 < 0 && (f1 = PI * 2 + f1); - f2 < 0 && (f2 = PI * 2 + f2); - if (sweep_flag && f1 > f2) { - f1 = f1 - PI * 2; - } - if (!sweep_flag && f2 > f1) { - f2 = f2 - PI * 2; - } - } else { - f1 = recursive[0]; - f2 = recursive[1]; - cx = recursive[2]; - cy = recursive[3]; - } - var df = f2 - f1; - if (abs(df) > _120) { - var f2old = f2, - x2old = x2, - y2old = y2; - f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); - x2 = cx + rx * math.cos(f2); - y2 = cy + ry * math.sin(f2); - res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); - } - df = f2 - f1; - var c1 = math.cos(f1), - s1 = math.sin(f1), - c2 = math.cos(f2), - s2 = math.sin(f2), - t = math.tan(df / 4), - hx = 4 / 3 * rx * t, - hy = 4 / 3 * ry * t, - m1 = [x1, y1], - m2 = [x1 + hx * s1, y1 - hy * c1], - m3 = [x2 + hx * s2, y2 - hy * c2], - m4 = [x2, y2]; - m2[0] = 2 * m1[0] - m2[0]; - m2[1] = 2 * m1[1] - m2[1]; - if (recursive) { - return [m2, m3, m4].concat(res); - } else { - res = [m2, m3, m4].concat(res).join().split(","); - var newres = []; - for (var i = 0, ii = res.length; i < ii; i++) { - newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; - } - return newres; - } - } - function findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t; - return { - x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, - y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y - }; - } - - // Returns bounding box of cubic bezier curve. - // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - // Original version: NISHIO Hirokazu - // Modifications: https://github.com/timo22345 - function curveDim(x0, y0, x1, y1, x2, y2, x3, y3) { - var tvalues = [], - bounds = [[], []], - a, b, c, t, t1, t2, b2ac, sqrtb2ac; - for (var i = 0; i < 2; ++i) { - if (i == 0) { - b = 6 * x0 - 12 * x1 + 6 * x2; - a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3; - c = 3 * x1 - 3 * x0; - } else { - b = 6 * y0 - 12 * y1 + 6 * y2; - a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3; - c = 3 * y1 - 3 * y0; - } - if (abs(a) < 1e-12) { - if (abs(b) < 1e-12) { - continue; - } - t = -c / b; - if (0 < t && t < 1) { - tvalues.push(t); - } - continue; - } - b2ac = b * b - 4 * c * a; - sqrtb2ac = math.sqrt(b2ac); - if (b2ac < 0) { - continue; - } - t1 = (-b + sqrtb2ac) / (2 * a); - if (0 < t1 && t1 < 1) { - tvalues.push(t1); - } - t2 = (-b - sqrtb2ac) / (2 * a); - if (0 < t2 && t2 < 1) { - tvalues.push(t2); - } - } - - var x, y, j = tvalues.length, - jlen = j, - mt; - while (j--) { - t = tvalues[j]; - mt = 1 - t; - bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3); - bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3); - } - - bounds[0][jlen] = x0; - bounds[1][jlen] = y0; - bounds[0][jlen + 1] = x3; - bounds[1][jlen + 1] = y3; - bounds[0].length = bounds[1].length = jlen + 2; - - - return { - min: {x: mmin.apply(0, bounds[0]), y: mmin.apply(0, bounds[1])}, - max: {x: mmax.apply(0, bounds[0]), y: mmax.apply(0, bounds[1])} - }; - } - - function path2curve(path, path2) { - var pth = !path2 && paths(path); - if (!path2 && pth.curve) { - return pathClone(pth.curve); - } - var p = pathToAbsolute(path), - p2 = path2 && pathToAbsolute(path2), - attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - processPath = function (path, d, pcom) { - var nx, ny; - if (!path) { - return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; - } - !(path[0] in {T: 1, Q: 1}) && (d.qx = d.qy = null); - switch (path[0]) { - case "M": - d.X = path[1]; - d.Y = path[2]; - break; - case "A": - path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1)))); - break; - case "S": - if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S. - nx = d.x * 2 - d.bx; // And reflect the previous - ny = d.y * 2 - d.by; // command's control point relative to the current point. - } - else { // or some else or nothing - nx = d.x; - ny = d.y; - } - path = ["C", nx, ny].concat(path.slice(1)); - break; - case "T": - if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T. - d.qx = d.x * 2 - d.qx; // And make a reflection similar - d.qy = d.y * 2 - d.qy; // to case "S". - } - else { // or something else or nothing - d.qx = d.x; - d.qy = d.y; - } - path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); - break; - case "Q": - d.qx = path[1]; - d.qy = path[2]; - path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4])); - break; - case "L": - path = ["C"].concat(l2c(d.x, d.y, path[1], path[2])); - break; - case "H": - path = ["C"].concat(l2c(d.x, d.y, path[1], d.y)); - break; - case "V": - path = ["C"].concat(l2c(d.x, d.y, d.x, path[1])); - break; - case "Z": - path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); - break; - } - return path; - }, - fixArc = function (pp, i) { - if (pp[i].length > 7) { - pp[i].shift(); - var pi = pp[i]; - while (pi.length) { - pcoms1[i] = "A"; // if created multiple C:s, their original seg is saved - p2 && (pcoms2[i] = "A"); // the same as above - pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6))); - } - pp.splice(i, 1); - ii = mmax(p.length, p2 && p2.length || 0); - } - }, - fixM = function (path1, path2, a1, a2, i) { - if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { - path2.splice(i, 0, ["M", a2.x, a2.y]); - a1.bx = 0; - a1.by = 0; - a1.x = path1[i][1]; - a1.y = path1[i][2]; - ii = mmax(p.length, p2 && p2.length || 0); - } - }, - pcoms1 = [], // path commands of original path p - pcoms2 = [], // path commands of original path p2 - pfirst = "", // temporary holder for original path command - pcom = ""; // holder for previous path command of original path - for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) { - p[i] && (pfirst = p[i][0]); // save current path command - - if (pfirst != "C") // C is not saved yet, because it may be result of conversion - { - pcoms1[i] = pfirst; // Save current path command - i && ( pcom = pcoms1[i - 1]); // Get previous path command pcom - } - p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath - - if (pcoms1[i] != "A" && pfirst == "C") pcoms1[i] = "C"; // A is the only command - // which may produce multiple C:s - // so we have to make sure that C is also C in original path - - fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1 - - if (p2) { // the same procedures is done to p2 - p2[i] && (pfirst = p2[i][0]); - if (pfirst != "C") { - pcoms2[i] = pfirst; - i && (pcom = pcoms2[i - 1]); - } - p2[i] = processPath(p2[i], attrs2, pcom); - - if (pcoms2[i] != "A" && pfirst == "C") { - pcoms2[i] = "C"; - } - - fixArc(p2, i); - } - fixM(p, p2, attrs, attrs2, i); - fixM(p2, p, attrs2, attrs, i); - var seg = p[i], - seg2 = p2 && p2[i], - seglen = seg.length, - seg2len = p2 && seg2.length; - attrs.x = seg[seglen - 2]; - attrs.y = seg[seglen - 1]; - attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; - attrs.by = toFloat(seg[seglen - 3]) || attrs.y; - attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); - attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); - attrs2.x = p2 && seg2[seg2len - 2]; - attrs2.y = p2 && seg2[seg2len - 1]; - } - if (!p2) { - pth.curve = pathClone(p); - } - return p2 ? [p, p2] : p; - } - function mapPath(path, matrix) { - if (!matrix) { - return path; - } - var x, y, i, j, ii, jj, pathi; - path = path2curve(path); - for (i = 0, ii = path.length; i < ii; i++) { - pathi = path[i]; - for (j = 1, jj = pathi.length; j < jj; j += 2) { - x = matrix.x(pathi[j], pathi[j + 1]); - y = matrix.y(pathi[j], pathi[j + 1]); - pathi[j] = x; - pathi[j + 1] = y; - } - } - return path; - } - - // http://schepers.cc/getting-to-the-point - function catmullRom2bezier(crp, z) { - var d = []; - for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { - var p = [ - {x: +crp[i - 2], y: +crp[i - 1]}, - {x: +crp[i], y: +crp[i + 1]}, - {x: +crp[i + 2], y: +crp[i + 3]}, - {x: +crp[i + 4], y: +crp[i + 5]} - ]; - if (z) { - if (!i) { - p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}; - } else if (iLen - 4 == i) { - p[3] = {x: +crp[0], y: +crp[1]}; - } else if (iLen - 2 == i) { - p[2] = {x: +crp[0], y: +crp[1]}; - p[3] = {x: +crp[2], y: +crp[3]}; - } - } else { - if (iLen - 4 == i) { - p[3] = p[2]; - } else if (!i) { - p[0] = {x: +crp[i], y: +crp[i + 1]}; - } - } - d.push(["C", - (-p[0].x + 6 * p[1].x + p[2].x) / 6, - (-p[0].y + 6 * p[1].y + p[2].y) / 6, - (p[1].x + 6 * p[2].x - p[3].x) / 6, - (p[1].y + 6*p[2].y - p[3].y) / 6, - p[2].x, - p[2].y - ]); - } - - return d; - } - - // export - Snap.path = paths; - - /*\ - * Snap.path.getTotalLength - [ method ] - ** - * Returns the length of the given path in pixels - ** - - path (string) SVG path string - ** - = (number) length - \*/ - Snap.path.getTotalLength = getTotalLength; - /*\ - * Snap.path.getPointAtLength - [ method ] - ** - * Returns the coordinates of the point located at the given length along the given path - ** - - path (string) SVG path string - - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps - ** - = (object) representation of the point: - o { - o x: (number) x coordinate, - o y: (number) y coordinate, - o alpha: (number) angle of derivative - o } - \*/ - Snap.path.getPointAtLength = getPointAtLength; - /*\ - * Snap.path.getSubpath - [ method ] - ** - * Returns the subpath of a given path between given start and end lengths - ** - - path (string) SVG path string - - from (number) length, in pixels, from the start of the path to the start of the segment - - to (number) length, in pixels, from the start of the path to the end of the segment - ** - = (string) path string definition for the segment - \*/ - Snap.path.getSubpath = function (path, from, to) { - if (this.getTotalLength(path) - to < 1e-6) { - return getSubpathsAtLength(path, from).end; - } - var a = getSubpathsAtLength(path, to, 1); - return from ? getSubpathsAtLength(a, from).end : a; - }; - /*\ - * Element.getTotalLength - [ method ] - ** - * Returns the length of the path in pixels (only works for `path` elements) - = (number) length - \*/ - elproto.getTotalLength = function () { - if (this.node.getTotalLength) { - return this.node.getTotalLength(); - } - }; - // SIERRA Element.getPointAtLength()/Element.getTotalLength(): If a is broken into different segments, is the jump distance to the new coordinates set by the _M_ or _m_ commands calculated as part of the path's total length? - /*\ - * Element.getPointAtLength - [ method ] - ** - * Returns coordinates of the point located at the given length on the given path (only works for `path` elements) - ** - - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps - ** - = (object) representation of the point: - o { - o x: (number) x coordinate, - o y: (number) y coordinate, - o alpha: (number) angle of derivative - o } - \*/ - elproto.getPointAtLength = function (length) { - return getPointAtLength(this.attr("d"), length); - }; - // SIERRA Element.getSubpath(): Similar to the problem for Element.getPointAtLength(). Unclear how this would work for a segmented path. Overall, the concept of _subpath_ and what I'm calling a _segment_ (series of non-_M_ or _Z_ commands) is unclear. - /*\ - * Element.getSubpath - [ method ] - ** - * Returns subpath of a given element from given start and end lengths (only works for `path` elements) - ** - - from (number) length, in pixels, from the start of the path to the start of the segment - - to (number) length, in pixels, from the start of the path to the end of the segment - ** - = (string) path string definition for the segment - \*/ - elproto.getSubpath = function (from, to) { - return Snap.path.getSubpath(this.attr("d"), from, to); - }; - Snap._.box = box; - /*\ - * Snap.path.findDotsAtSegment - [ method ] - ** - * Utility method - ** - * Finds dot coordinates on the given cubic beziér curve at the given t - - p1x (number) x of the first point of the curve - - p1y (number) y of the first point of the curve - - c1x (number) x of the first anchor of the curve - - c1y (number) y of the first anchor of the curve - - c2x (number) x of the second anchor of the curve - - c2y (number) y of the second anchor of the curve - - p2x (number) x of the second point of the curve - - p2y (number) y of the second point of the curve - - t (number) position on the curve (0..1) - = (object) point information in format: - o { - o x: (number) x coordinate of the point, - o y: (number) y coordinate of the point, - o m: { - o x: (number) x coordinate of the left anchor, - o y: (number) y coordinate of the left anchor - o }, - o n: { - o x: (number) x coordinate of the right anchor, - o y: (number) y coordinate of the right anchor - o }, - o start: { - o x: (number) x coordinate of the start of the curve, - o y: (number) y coordinate of the start of the curve - o }, - o end: { - o x: (number) x coordinate of the end of the curve, - o y: (number) y coordinate of the end of the curve - o }, - o alpha: (number) angle of the curve derivative at the point - o } - \*/ - Snap.path.findDotsAtSegment = findDotsAtSegment; - /*\ - * Snap.path.bezierBBox - [ method ] - ** - * Utility method - ** - * Returns the bounding box of a given cubic beziér curve - - p1x (number) x of the first point of the curve - - p1y (number) y of the first point of the curve - - c1x (number) x of the first anchor of the curve - - c1y (number) y of the first anchor of the curve - - c2x (number) x of the second anchor of the curve - - c2y (number) y of the second anchor of the curve - - p2x (number) x of the second point of the curve - - p2y (number) y of the second point of the curve - * or - - bez (array) array of six points for beziér curve - = (object) bounding box - o { - o x: (number) x coordinate of the left top point of the box, - o y: (number) y coordinate of the left top point of the box, - o x2: (number) x coordinate of the right bottom point of the box, - o y2: (number) y coordinate of the right bottom point of the box, - o width: (number) width of the box, - o height: (number) height of the box - o } - \*/ - Snap.path.bezierBBox = bezierBBox; - /*\ - * Snap.path.isPointInsideBBox - [ method ] - ** - * Utility method - ** - * Returns `true` if given point is inside bounding box - - bbox (string) bounding box - - x (string) x coordinate of the point - - y (string) y coordinate of the point - = (boolean) `true` if point is inside - \*/ - Snap.path.isPointInsideBBox = isPointInsideBBox; - Snap.closest = function (x, y, X, Y) { - var r = 100, - b = box(x - r / 2, y - r / 2, r, r), - inside = [], - getter = X[0].hasOwnProperty("x") ? function (i) { - return { - x: X[i].x, - y: X[i].y - }; - } : function (i) { - return { - x: X[i], - y: Y[i] - }; - }, - found = 0; - while (r <= 1e6 && !found) { - for (var i = 0, ii = X.length; i < ii; i++) { - var xy = getter(i); - if (isPointInsideBBox(b, xy.x, xy.y)) { - found++; - inside.push(xy); - break; - } - } - if (!found) { - r *= 2; - b = box(x - r / 2, y - r / 2, r, r) - } - } - if (r == 1e6) { - return; - } - var len = Infinity, - res; - for (i = 0, ii = inside.length; i < ii; i++) { - var l = Snap.len(x, y, inside[i].x, inside[i].y); - if (len > l) { - len = l; - inside[i].len = l; - res = inside[i]; - } - } - return res; - }; - /*\ - * Snap.path.isBBoxIntersect - [ method ] - ** - * Utility method - ** - * Returns `true` if two bounding boxes intersect - - bbox1 (string) first bounding box - - bbox2 (string) second bounding box - = (boolean) `true` if bounding boxes intersect - \*/ - Snap.path.isBBoxIntersect = isBBoxIntersect; - /*\ - * Snap.path.intersection - [ method ] - ** - * Utility method - ** - * Finds intersections of two paths - - path1 (string) path string - - path2 (string) path string - = (array) dots of intersection - o [ - o { - o x: (number) x coordinate of the point, - o y: (number) y coordinate of the point, - o t1: (number) t value for segment of path1, - o t2: (number) t value for segment of path2, - o segment1: (number) order number for segment of path1, - o segment2: (number) order number for segment of path2, - o bez1: (array) eight coordinates representing beziér curve for the segment of path1, - o bez2: (array) eight coordinates representing beziér curve for the segment of path2 - o } - o ] - \*/ - Snap.path.intersection = pathIntersection; - Snap.path.intersectionNumber = pathIntersectionNumber; - /*\ - * Snap.path.isPointInside - [ method ] - ** - * Utility method - ** - * Returns `true` if given point is inside a given closed path. - * - * Note: fill mode doesn’t affect the result of this method. - - path (string) path string - - x (number) x of the point - - y (number) y of the point - = (boolean) `true` if point is inside the path - \*/ - Snap.path.isPointInside = isPointInsidePath; - /*\ - * Snap.path.getBBox - [ method ] - ** - * Utility method - ** - * Returns the bounding box of a given path - - path (string) path string - = (object) bounding box - o { - o x: (number) x coordinate of the left top point of the box, - o y: (number) y coordinate of the left top point of the box, - o x2: (number) x coordinate of the right bottom point of the box, - o y2: (number) y coordinate of the right bottom point of the box, - o width: (number) width of the box, - o height: (number) height of the box - o } - \*/ - Snap.path.getBBox = pathBBox; - Snap.path.get = getPath; - /*\ - * Snap.path.toRelative - [ method ] - ** - * Utility method - ** - * Converts path coordinates into relative values - - path (string) path string - = (array) path string - \*/ - Snap.path.toRelative = pathToRelative; - /*\ - * Snap.path.toAbsolute - [ method ] - ** - * Utility method - ** - * Converts path coordinates into absolute values - - path (string) path string - = (array) path string - \*/ - Snap.path.toAbsolute = pathToAbsolute; - /*\ - * Snap.path.toCubic - [ method ] - ** - * Utility method - ** - * Converts path to a new path where all segments are cubic beziér curves - - pathString (string|array) path string or array of segments - = (array) array of segments - \*/ - Snap.path.toCubic = path2curve; - /*\ - * Snap.path.map - [ method ] - ** - * Transform the path string with the given matrix - - path (string) path string - - matrix (object) see @Matrix - = (string) transformed path string - \*/ - Snap.path.map = mapPath; - Snap.path.toString = toString; - Snap.path.clone = pathClone; -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob) { - var mmax = Math.max, - mmin = Math.min; - - // Set - var Set = function (items) { - this.items = []; - this.bindings = {}; - this.length = 0; - this.type = "set"; - if (items) { - for (var i = 0, ii = items.length; i < ii; i++) { - if (items[i]) { - this[this.items.length] = this.items[this.items.length] = items[i]; - this.length++; - } - } - } - }, - setproto = Set.prototype; - /*\ - * Set.push - [ method ] - ** - * Adds each argument to the current set - = (object) original element - \*/ - setproto.push = function () { - var item, - len; - for (var i = 0, ii = arguments.length; i < ii; i++) { - item = arguments[i]; - if (item) { - len = this.items.length; - this[len] = this.items[len] = item; - this.length++; - } - } - return this; - }; - /*\ - * Set.pop - [ method ] - ** - * Removes last element and returns it - = (object) element - \*/ - setproto.pop = function () { - this.length && delete this[this.length--]; - return this.items.pop(); - }; - /*\ - * Set.forEach - [ method ] - ** - * Executes given function for each element in the set - * - * If the function returns `false`, the loop stops running. - ** - - callback (function) function to run - - thisArg (object) context object for the callback - = (object) Set object - \*/ - setproto.forEach = function (callback, thisArg) { - for (var i = 0, ii = this.items.length; i < ii; i++) { - if (callback.call(thisArg, this.items[i], i) === false) { - return this; - } - } - return this; - }; - /*\ - * Set.animate - [ method ] - ** - * Animates each element in set in sync. - * - ** - - attrs (object) key-value pairs of destination attributes - - duration (number) duration of the animation in milliseconds - - easing (function) #optional easing function from @mina or custom - - callback (function) #optional callback function that executes when the animation ends - * or - - animation (array) array of animation parameter for each element in set in format `[attrs, duration, easing, callback]` - > Usage - | // animate all elements in set to radius 10 - | set.animate({r: 10}, 500, mina.easein); - | // or - | // animate first element to radius 10, but second to radius 20 and in different time - | set.animate([{r: 10}, 500, mina.easein], [{r: 20}, 1500, mina.easein]); - = (Element) the current element - \*/ - setproto.animate = function (attrs, ms, easing, callback) { - if (typeof easing == "function" && !easing.length) { - callback = easing; - easing = mina.linear; - } - if (attrs instanceof Snap._.Animation) { - callback = attrs.callback; - easing = attrs.easing; - ms = easing.dur; - attrs = attrs.attr; - } - var args = arguments; - if (Snap.is(attrs, "array") && Snap.is(args[args.length - 1], "array")) { - var each = true; - } - var begin, - handler = function () { - if (begin) { - this.b = begin; - } else { - begin = this.b; - } - }, - cb = 0, - set = this, - callbacker = callback && function () { - if (++cb == set.length) { - callback.call(this); - } - }; - return this.forEach(function (el, i) { - eve.once("snap.animcreated." + el.id, handler); - if (each) { - args[i] && el.animate.apply(el, args[i]); - } else { - el.animate(attrs, ms, easing, callbacker); - } - }); - }; - setproto.remove = function () { - while (this.length) { - this.pop().remove(); - } - return this; - }; - /*\ - * Set.bind - [ method ] - ** - * Specifies how to handle a specific attribute when applied - * to a set. - * - ** - - attr (string) attribute name - - callback (function) function to run - * or - - attr (string) attribute name - - element (Element) specific element in the set to apply the attribute to - * or - - attr (string) attribute name - - element (Element) specific element in the set to apply the attribute to - - eattr (string) attribute on the element to bind the attribute to - = (object) Set object - \*/ - setproto.bind = function (attr, a, b) { - var data = {}; - if (typeof a == "function") { - this.bindings[attr] = a; - } else { - var aname = b || attr; - this.bindings[attr] = function (v) { - data[aname] = v; - a.attr(data); - }; - } - return this; - }; - setproto.attr = function (value) { - var unbound = {}; - for (var k in value) { - if (this.bindings[k]) { - this.bindings[k](value[k]); - } else { - unbound[k] = value[k]; - } - } - for (var i = 0, ii = this.items.length; i < ii; i++) { - this.items[i].attr(unbound); - } - return this; - }; - /*\ - * Set.clear - [ method ] - ** - * Removes all elements from the set - \*/ - setproto.clear = function () { - while (this.length) { - this.pop(); - } - }; - /*\ - * Set.splice - [ method ] - ** - * Removes range of elements from the set - ** - - index (number) position of the deletion - - count (number) number of element to remove - - insertion… (object) #optional elements to insert - = (object) set elements that were deleted - \*/ - setproto.splice = function (index, count, insertion) { - index = index < 0 ? mmax(this.length + index, 0) : index; - count = mmax(0, mmin(this.length - index, count)); - var tail = [], - todel = [], - args = [], - i; - for (i = 2; i < arguments.length; i++) { - args.push(arguments[i]); - } - for (i = 0; i < count; i++) { - todel.push(this[index + i]); - } - for (; i < this.length - index; i++) { - tail.push(this[index + i]); - } - var arglen = args.length; - for (i = 0; i < arglen + tail.length; i++) { - this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen]; - } - i = this.items.length = this.length -= count - arglen; - while (this[i]) { - delete this[i++]; - } - return new Set(todel); - }; - /*\ - * Set.exclude - [ method ] - ** - * Removes given element from the set - ** - - element (object) element to remove - = (boolean) `true` if object was found and removed from the set - \*/ - setproto.exclude = function (el) { - for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) { - this.splice(i, 1); - return true; - } - return false; - }; - setproto.insertAfter = function (el) { - var i = this.items.length; - while (i--) { - this.items[i].insertAfter(el); - } - return this; - }; - setproto.getBBox = function () { - var x = [], - y = [], - x2 = [], - y2 = []; - for (var i = this.items.length; i--;) if (!this.items[i].removed) { - var box = this.items[i].getBBox(); - x.push(box.x); - y.push(box.y); - x2.push(box.x + box.width); - y2.push(box.y + box.height); - } - x = mmin.apply(0, x); - y = mmin.apply(0, y); - x2 = mmax.apply(0, x2); - y2 = mmax.apply(0, y2); - return { - x: x, - y: y, - x2: x2, - y2: y2, - width: x2 - x, - height: y2 - y, - cx: x + (x2 - x) / 2, - cy: y + (y2 - y) / 2 - }; - }; - setproto.clone = function (s) { - s = new Set; - for (var i = 0, ii = this.items.length; i < ii; i++) { - s.push(this.items[i].clone()); - } - return s; - }; - setproto.toString = function () { - return "Snap\u2018s set"; - }; - setproto.type = "set"; - // export - Snap.Set = Set; - Snap.set = function () { - var set = new Set; - if (arguments.length) { - set.push.apply(set, Array.prototype.slice.call(arguments, 0)); - } - return set; - }; -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob) { - var names = {}, - reUnit = /[a-z]+$/i, - Str = String; - names.stroke = names.fill = "colour"; - function getEmpty(item) { - var l = item[0]; - switch (l.toLowerCase()) { - case "t": return [l, 0, 0]; - case "m": return [l, 1, 0, 0, 1, 0, 0]; - case "r": if (item.length == 4) { - return [l, 0, item[2], item[3]]; - } else { - return [l, 0]; - } - case "s": if (item.length == 5) { - return [l, 1, 1, item[3], item[4]]; - } else if (item.length == 3) { - return [l, 1, 1]; - } else { - return [l, 1]; - } - } - } - function equaliseTransform(t1, t2, getBBox) { - t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); - t1 = Snap.parseTransformString(t1) || []; - t2 = Snap.parseTransformString(t2) || []; - var maxlength = Math.max(t1.length, t2.length), - from = [], - to = [], - i = 0, j, jj, - tt1, tt2; - for (; i < maxlength; i++) { - tt1 = t1[i] || getEmpty(t2[i]); - tt2 = t2[i] || getEmpty(tt1); - if ((tt1[0] != tt2[0]) || - (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || - (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) - ) { - t1 = Snap._.transform2matrix(t1, getBBox()); - t2 = Snap._.transform2matrix(t2, getBBox()); - from = [["m", t1.a, t1.b, t1.c, t1.d, t1.e, t1.f]]; - to = [["m", t2.a, t2.b, t2.c, t2.d, t2.e, t2.f]]; - break; - } - from[i] = []; - to[i] = []; - for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) { - j in tt1 && (from[i][j] = tt1[j]); - j in tt2 && (to[i][j] = tt2[j]); - } - } - return { - from: path2array(from), - to: path2array(to), - f: getPath(from) - }; - } - function getNumber(val) { - return val; - } - function getUnit(unit) { - return function (val) { - return +val.toFixed(3) + unit; - }; - } - function getViewBox(val) { - return val.join(" "); - } - function getColour(clr) { - return Snap.rgb(clr[0], clr[1], clr[2]); - } - function getPath(path) { - var k = 0, i, ii, j, jj, out, a, b = []; - for (i = 0, ii = path.length; i < ii; i++) { - out = "["; - a = ['"' + path[i][0] + '"']; - for (j = 1, jj = path[i].length; j < jj; j++) { - a[j] = "val[" + (k++) + "]"; - } - out += a + "]"; - b[i] = out; - } - return Function("val", "return Snap.path.toString.call([" + b + "])"); - } - function path2array(path) { - var out = []; - for (var i = 0, ii = path.length; i < ii; i++) { - for (var j = 1, jj = path[i].length; j < jj; j++) { - out.push(path[i][j]); - } - } - return out; - } - function isNumeric(obj) { - return isFinite(parseFloat(obj)); - } - function arrayEqual(arr1, arr2) { - if (!Snap.is(arr1, "array") || !Snap.is(arr2, "array")) { - return false; - } - return arr1.toString() == arr2.toString(); - } - Element.prototype.equal = function (name, b) { - return eve("snap.util.equal", this, name, b).firstDefined(); - }; - eve.on("snap.util.equal", function (name, b) { - var A, B, a = Str(this.attr(name) || ""), - el = this; - if (isNumeric(a) && isNumeric(b)) { - return { - from: parseFloat(a), - to: parseFloat(b), - f: getNumber - }; - } - if (names[name] == "colour") { - A = Snap.color(a); - B = Snap.color(b); - return { - from: [A.r, A.g, A.b, A.opacity], - to: [B.r, B.g, B.b, B.opacity], - f: getColour - }; - } - if (name == "viewBox") { - A = this.attr(name).vb.split(" ").map(Number); - B = b.split(" ").map(Number); - return { - from: A, - to: B, - f: getViewBox - }; - } - if (name == "transform" || name == "gradientTransform" || name == "patternTransform") { - if (b instanceof Snap.Matrix) { - b = b.toTransformString(); - } - if (!Snap._.rgTransform.test(b)) { - b = Snap._.svgTransform2string(b); - } - return equaliseTransform(a, b, function () { - return el.getBBox(1); - }); - } - if (name == "d" || name == "path") { - A = Snap.path.toCubic(a, b); - return { - from: path2array(A[0]), - to: path2array(A[1]), - f: getPath(A[0]) - }; - } - if (name == "points") { - A = Str(a).split(Snap._.separator); - B = Str(b).split(Snap._.separator); - return { - from: A, - to: B, - f: function (val) { return val; } - }; - } - var aUnit = a.match(reUnit), - bUnit = Str(b).match(reUnit); - if (aUnit && arrayEqual(aUnit, bUnit)) { - return { - from: parseFloat(a), - to: parseFloat(b), - f: getUnit(aUnit) - }; - } else { - return { - from: this.asPX(name), - to: this.asPX(name, b), - f: getNumber - }; - } - }); -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob) { - var elproto = Element.prototype, - has = "hasOwnProperty", - supportsTouch = "createTouch" in glob.doc, - events = [ - "click", "dblclick", "mousedown", "mousemove", "mouseout", - "mouseover", "mouseup", "touchstart", "touchmove", "touchend", - "touchcancel" - ], - touchMap = { - mousedown: "touchstart", - mousemove: "touchmove", - mouseup: "touchend" - }, - getScroll = function (xy, el) { - var name = xy == "y" ? "scrollTop" : "scrollLeft", - doc = el && el.node ? el.node.ownerDocument : glob.doc; - return doc[name in doc.documentElement ? "documentElement" : "body"][name]; - }, - preventDefault = function () { - this.returnValue = false; - }, - preventTouch = function () { - return this.originalEvent.preventDefault(); - }, - stopPropagation = function () { - this.cancelBubble = true; - }, - stopTouch = function () { - return this.originalEvent.stopPropagation(); - }, - addEvent = function (obj, type, fn, element) { - var realName = supportsTouch && touchMap[type] ? touchMap[type] : type, - f = function (e) { - var scrollY = getScroll("y", element), - scrollX = getScroll("x", element); - if (supportsTouch && touchMap[has](type)) { - for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { - if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) { - var olde = e; - e = e.targetTouches[i]; - e.originalEvent = olde; - e.preventDefault = preventTouch; - e.stopPropagation = stopTouch; - break; - } - } - } - var x = e.clientX + scrollX, - y = e.clientY + scrollY; - return fn.call(element, e, x, y); - }; - - if (type !== realName) { - obj.addEventListener(type, f, false); - } - - obj.addEventListener(realName, f, false); - - return function () { - if (type !== realName) { - obj.removeEventListener(type, f, false); - } - - obj.removeEventListener(realName, f, false); - return true; - }; - }, - drag = [], - dragMove = function (e) { - var x = e.clientX, - y = e.clientY, - scrollY = getScroll("y"), - scrollX = getScroll("x"), - dragi, - j = drag.length; - while (j--) { - dragi = drag[j]; - if (supportsTouch) { - var i = e.touches && e.touches.length, - touch; - while (i--) { - touch = e.touches[i]; - if (touch.identifier == dragi.el._drag.id || dragi.el.node.contains(touch.target)) { - x = touch.clientX; - y = touch.clientY; - (e.originalEvent ? e.originalEvent : e).preventDefault(); - break; - } - } - } else { - e.preventDefault(); - } - var node = dragi.el.node, - o, - next = node.nextSibling, - parent = node.parentNode, - display = node.style.display; - // glob.win.opera && parent.removeChild(node); - // node.style.display = "none"; - // o = dragi.el.paper.getElementByPoint(x, y); - // node.style.display = display; - // glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); - // o && eve("snap.drag.over." + dragi.el.id, dragi.el, o); - x += scrollX; - y += scrollY; - eve("snap.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); - } - }, - dragUp = function (e) { - Snap.unmousemove(dragMove).unmouseup(dragUp); - var i = drag.length, - dragi; - while (i--) { - dragi = drag[i]; - dragi.el._drag = {}; - eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); - eve.off("snap.drag.*." + dragi.el.id); - } - drag = []; - }; - /*\ - * Element.click - [ method ] - ** - * Adds a click event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unclick - [ method ] - ** - * Removes a click event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.dblclick - [ method ] - ** - * Adds a double click event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.undblclick - [ method ] - ** - * Removes a double click event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.mousedown - [ method ] - ** - * Adds a mousedown event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unmousedown - [ method ] - ** - * Removes a mousedown event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.mousemove - [ method ] - ** - * Adds a mousemove event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unmousemove - [ method ] - ** - * Removes a mousemove event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.mouseout - [ method ] - ** - * Adds a mouseout event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unmouseout - [ method ] - ** - * Removes a mouseout event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.mouseover - [ method ] - ** - * Adds a mouseover event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unmouseover - [ method ] - ** - * Removes a mouseover event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.mouseup - [ method ] - ** - * Adds a mouseup event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.unmouseup - [ method ] - ** - * Removes a mouseup event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.touchstart - [ method ] - ** - * Adds a touchstart event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.untouchstart - [ method ] - ** - * Removes a touchstart event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.touchmove - [ method ] - ** - * Adds a touchmove event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.untouchmove - [ method ] - ** - * Removes a touchmove event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.touchend - [ method ] - ** - * Adds a touchend event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.untouchend - [ method ] - ** - * Removes a touchend event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - - /*\ - * Element.touchcancel - [ method ] - ** - * Adds a touchcancel event handler to the element - - handler (function) handler for the event - = (object) @Element - \*/ - /*\ - * Element.untouchcancel - [ method ] - ** - * Removes a touchcancel event handler from the element - - handler (function) handler for the event - = (object) @Element - \*/ - for (var i = events.length; i--;) { - (function (eventName) { - Snap[eventName] = elproto[eventName] = function (fn, scope) { - if (Snap.is(fn, "function")) { - this.events = this.events || []; - this.events.push({ - name: eventName, - f: fn, - unbind: addEvent(this.node || document, eventName, fn, scope || this) - }); - } else { - for (var i = 0, ii = this.events.length; i < ii; i++) if (this.events[i].name == eventName) { - try { - this.events[i].f.call(this); - } catch (e) {} - } - } - return this; - }; - Snap["un" + eventName] = - elproto["un" + eventName] = function (fn) { - var events = this.events || [], - l = events.length; - while (l--) if (events[l].name == eventName && - (events[l].f == fn || !fn)) { - events[l].unbind(); - events.splice(l, 1); - !events.length && delete this.events; - return this; - } - return this; - }; - })(events[i]); - } - /*\ - * Element.hover - [ method ] - ** - * Adds hover event handlers to the element - - f_in (function) handler for hover in - - f_out (function) handler for hover out - - icontext (object) #optional context for hover in handler - - ocontext (object) #optional context for hover out handler - = (object) @Element - \*/ - elproto.hover = function (f_in, f_out, scope_in, scope_out) { - return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); - }; - /*\ - * Element.unhover - [ method ] - ** - * Removes hover event handlers from the element - - f_in (function) handler for hover in - - f_out (function) handler for hover out - = (object) @Element - \*/ - elproto.unhover = function (f_in, f_out) { - return this.unmouseover(f_in).unmouseout(f_out); - }; - var draggable = []; - // SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture. - // SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from? - // SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason. - // SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start. on start, drag.end. on end and drag.move. on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID? - /*\ - * Element.drag - [ method ] - ** - * Adds event handlers for an element's drag gesture - ** - - onmove (function) handler for moving - - onstart (function) handler for drag start - - onend (function) handler for drag end - - mcontext (object) #optional context for moving handler - - scontext (object) #optional context for drag start handler - - econtext (object) #optional context for drag end handler - * Additionaly following `drag` events are triggered: `drag.start.` on start, - * `drag.end.` on end and `drag.move.` on every move. When element is dragged over another element - * `drag.over.` fires as well. - * - * Start event and start handler are called in specified context or in context of the element with following parameters: - o x (number) x position of the mouse - o y (number) y position of the mouse - o event (object) DOM event object - * Move event and move handler are called in specified context or in context of the element with following parameters: - o dx (number) shift by x from the start point - o dy (number) shift by y from the start point - o x (number) x position of the mouse - o y (number) y position of the mouse - o event (object) DOM event object - * End event and end handler are called in specified context or in context of the element with following parameters: - o event (object) DOM event object - = (object) @Element - \*/ - elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { - var el = this; - if (!arguments.length) { - var origTransform; - return el.drag(function (dx, dy) { - this.attr({ - transform: origTransform + (origTransform ? "T" : "t") + [dx, dy] - }); - }, function () { - origTransform = this.transform().local; - }); - } - function start(e, x, y) { - (e.originalEvent || e).preventDefault(); - el._drag.x = x; - el._drag.y = y; - el._drag.id = e.identifier; - !drag.length && Snap.mousemove(dragMove).mouseup(dragUp); - drag.push({el: el, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); - onstart && eve.on("snap.drag.start." + el.id, onstart); - onmove && eve.on("snap.drag.move." + el.id, onmove); - onend && eve.on("snap.drag.end." + el.id, onend); - eve("snap.drag.start." + el.id, start_scope || move_scope || el, x, y, e); - } - function init(e, x, y) { - eve("snap.draginit." + el.id, el, e, x, y); - } - eve.on("snap.draginit." + el.id, start); - el._drag = {}; - draggable.push({el: el, start: start, init: init}); - el.mousedown(init); - return el; - }; - /* - * Element.onDragOver - [ method ] - ** - * Shortcut to assign event handler for `drag.over.` event, where `id` is the element's `id` (see @Element.id) - - f (function) handler for event, first argument would be the element you are dragging over - \*/ - // elproto.onDragOver = function (f) { - // f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id); - // }; - /*\ - * Element.undrag - [ method ] - ** - * Removes all drag event handlers from the given element - \*/ - elproto.undrag = function () { - var i = draggable.length; - while (i--) if (draggable[i].el == this) { - this.unmousedown(draggable[i].init); - draggable.splice(i, 1); - eve.unbind("snap.drag.*." + this.id); - eve.unbind("snap.draginit." + this.id); - } - !draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp); - return this; - }; -}); - -// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob) { - var elproto = Element.prototype, - pproto = Paper.prototype, - rgurl = /^\s*url\((.+)\)/, - Str = String, - $ = Snap._.$; - Snap.filter = {}; - /*\ - * Paper.filter - [ method ] - ** - * Creates a `` element - ** - - filstr (string) SVG fragment of filter provided as a string - = (object) @Element - * Note: It is recommended to use filters embedded into the page inside an empty SVG element. - > Usage - | var f = paper.filter(''), - | c = paper.circle(10, 10, 10).attr({ - | filter: f - | }); - \*/ - pproto.filter = function (filstr) { - var paper = this; - if (paper.type != "svg") { - paper = paper.paper; - } - var f = Snap.parse(Str(filstr)), - id = Snap._.id(), - width = paper.node.offsetWidth, - height = paper.node.offsetHeight, - filter = $("filter"); - $(filter, { - id: id, - filterUnits: "userSpaceOnUse" - }); - filter.appendChild(f.node); - paper.defs.appendChild(filter); - return new Element(filter); - }; - - eve.on("snap.util.getattr.filter", function () { - eve.stop(); - var p = $(this.node, "filter"); - if (p) { - var match = Str(p).match(rgurl); - return match && Snap.select(match[1]); - } - }); - eve.on("snap.util.attr.filter", function (value) { - if (value instanceof Element && value.type == "filter") { - eve.stop(); - var id = value.node.id; - if (!id) { - $(value.node, {id: value.id}); - id = value.id; - } - $(this.node, { - filter: Snap.url(id) - }); - } - if (!value || value == "none") { - eve.stop(); - this.node.removeAttribute("filter"); - } - }); - /*\ - * Snap.filter.blur - [ method ] - ** - * Returns an SVG markup string for the blur filter - ** - - x (number) amount of horizontal blur, in pixels - - y (number) #optional amount of vertical blur, in pixels - = (string) filter representation - > Usage - | var f = paper.filter(Snap.filter.blur(5, 10)), - | c = paper.circle(10, 10, 10).attr({ - | filter: f - | }); - \*/ - Snap.filter.blur = function (x, y) { - if (x == null) { - x = 2; - } - var def = y == null ? x : [x, y]; - return Snap.format('\', { - def: def - }); - }; - Snap.filter.blur.toString = function () { - return this(); - }; - /*\ - * Snap.filter.shadow - [ method ] - ** - * Returns an SVG markup string for the shadow filter - ** - - dx (number) #optional horizontal shift of the shadow, in pixels - - dy (number) #optional vertical shift of the shadow, in pixels - - blur (number) #optional amount of blur - - color (string) #optional color of the shadow - - opacity (number) #optional `0..1` opacity of the shadow - * or - - dx (number) #optional horizontal shift of the shadow, in pixels - - dy (number) #optional vertical shift of the shadow, in pixels - - color (string) #optional color of the shadow - - opacity (number) #optional `0..1` opacity of the shadow - * which makes blur default to `4`. Or - - dx (number) #optional horizontal shift of the shadow, in pixels - - dy (number) #optional vertical shift of the shadow, in pixels - - opacity (number) #optional `0..1` opacity of the shadow - = (string) filter representation - > Usage - | var f = paper.filter(Snap.filter.shadow(0, 2, 3)), - | c = paper.circle(10, 10, 10).attr({ - | filter: f - | }); - \*/ - Snap.filter.shadow = function (dx, dy, blur, color, opacity) { - if (typeof blur == "string") { - color = blur; - opacity = color; - blur = 4; - } - if (typeof color != "string") { - opacity = color; - color = "#000"; - } - color = color || "#000"; - if (blur == null) { - blur = 4; - } - if (opacity == null) { - opacity = 1; - } - if (dx == null) { - dx = 0; - dy = 2; - } - if (dy == null) { - dy = dx; - } - color = Snap.color(color); - return Snap.format('', { - color: color, - dx: dx, - dy: dy, - blur: blur, - opacity: opacity - }); - }; - Snap.filter.shadow.toString = function () { - return this(); - }; - /*\ - * Snap.filter.grayscale - [ method ] - ** - * Returns an SVG markup string for the grayscale filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.grayscale = function (amount) { - if (amount == null) { - amount = 1; - } - return Snap.format('', { - a: 0.2126 + 0.7874 * (1 - amount), - b: 0.7152 - 0.7152 * (1 - amount), - c: 0.0722 - 0.0722 * (1 - amount), - d: 0.2126 - 0.2126 * (1 - amount), - e: 0.7152 + 0.2848 * (1 - amount), - f: 0.0722 - 0.0722 * (1 - amount), - g: 0.2126 - 0.2126 * (1 - amount), - h: 0.0722 + 0.9278 * (1 - amount) - }); - }; - Snap.filter.grayscale.toString = function () { - return this(); - }; - /*\ - * Snap.filter.sepia - [ method ] - ** - * Returns an SVG markup string for the sepia filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.sepia = function (amount) { - if (amount == null) { - amount = 1; - } - return Snap.format('', { - a: 0.393 + 0.607 * (1 - amount), - b: 0.769 - 0.769 * (1 - amount), - c: 0.189 - 0.189 * (1 - amount), - d: 0.349 - 0.349 * (1 - amount), - e: 0.686 + 0.314 * (1 - amount), - f: 0.168 - 0.168 * (1 - amount), - g: 0.272 - 0.272 * (1 - amount), - h: 0.534 - 0.534 * (1 - amount), - i: 0.131 + 0.869 * (1 - amount) - }); - }; - Snap.filter.sepia.toString = function () { - return this(); - }; - /*\ - * Snap.filter.saturate - [ method ] - ** - * Returns an SVG markup string for the saturate filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.saturate = function (amount) { - if (amount == null) { - amount = 1; - } - return Snap.format('', { - amount: 1 - amount - }); - }; - Snap.filter.saturate.toString = function () { - return this(); - }; - /*\ - * Snap.filter.hueRotate - [ method ] - ** - * Returns an SVG markup string for the hue-rotate filter - ** - - angle (number) angle of rotation - = (string) filter representation - \*/ - Snap.filter.hueRotate = function (angle) { - angle = angle || 0; - return Snap.format('', { - angle: angle - }); - }; - Snap.filter.hueRotate.toString = function () { - return this(); - }; - /*\ - * Snap.filter.invert - [ method ] - ** - * Returns an SVG markup string for the invert filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.invert = function (amount) { - if (amount == null) { - amount = 1; - } -// - return Snap.format('', { - amount: amount, - amount2: 1 - amount - }); - }; - Snap.filter.invert.toString = function () { - return this(); - }; - /*\ - * Snap.filter.brightness - [ method ] - ** - * Returns an SVG markup string for the brightness filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.brightness = function (amount) { - if (amount == null) { - amount = 1; - } - return Snap.format('', { - amount: amount - }); - }; - Snap.filter.brightness.toString = function () { - return this(); - }; - /*\ - * Snap.filter.contrast - [ method ] - ** - * Returns an SVG markup string for the contrast filter - ** - - amount (number) amount of filter (`0..1`) - = (string) filter representation - \*/ - Snap.filter.contrast = function (amount) { - if (amount == null) { - amount = 1; - } - return Snap.format('', { - amount: amount, - amount2: .5 - amount / 2 - }); - }; - Snap.filter.contrast.toString = function () { - return this(); - }; -}); - -// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { - var box = Snap._.box, - is = Snap.is, - firstLetter = /^[^a-z]*([tbmlrc])/i, - toString = function () { - return "T" + this.dx + "," + this.dy; - }; - /*\ - * Element.getAlign - [ method ] - ** - * Returns shift needed to align the element relatively to given element. - * If no elements specified, parent `` container will be used. - - el (object) @optional alignment element - - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"` - = (object|string) Object in format `{dx: , dy: }` also has a string representation as a transformation string - > Usage - | el.transform(el.getAlign(el2, "top")); - * or - | var dy = el.getAlign(el2, "top").dy; - \*/ - Element.prototype.getAlign = function (el, way) { - if (way == null && is(el, "string")) { - way = el; - el = null; - } - el = el || this.paper; - var bx = el.getBBox ? el.getBBox() : box(el), - bb = this.getBBox(), - out = {}; - way = way && way.match(firstLetter); - way = way ? way[1].toLowerCase() : "c"; - switch (way) { - case "t": - out.dx = 0; - out.dy = bx.y - bb.y; - break; - case "b": - out.dx = 0; - out.dy = bx.y2 - bb.y2; - break; - case "m": - out.dx = 0; - out.dy = bx.cy - bb.cy; - break; - case "l": - out.dx = bx.x - bb.x; - out.dy = 0; - break; - case "r": - out.dx = bx.x2 - bb.x2; - out.dy = 0; - break; - default: - out.dx = bx.cx - bb.cx; - out.dy = 0; - break; - } - out.toString = toString; - return out; - }; - /*\ - * Element.align - [ method ] - ** - * Aligns the element relatively to given one via transformation. - * If no elements specified, parent `` container will be used. - - el (object) @optional alignment element - - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"` - = (object) this element - > Usage - | el.align(el2, "top"); - * or - | el.align("middle"); - \*/ - Element.prototype.align = function (el, way) { - return this.transform("..." + this.getAlign(el, way)); - }; -}); - -return Snap; -})); diff --git a/web/pgadmin/preferences/templates/preferences/preferences.js b/web/pgadmin/preferences/templates/preferences/preferences.js deleted file mode 100644 index c785fbb5..00000000 --- a/web/pgadmin/preferences/templates/preferences/preferences.js +++ /dev/null @@ -1,415 +0,0 @@ -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 = $("
"); - - - /* - * 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( - "
" - ).append( - "
" + - gettext('Category is not selected.') + - "
" - ); - - // 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/templates/settings/settings.js b/web/pgadmin/settings/templates/settings/settings.js deleted file mode 100644 index 989de7f8..00000000 --- a/web/pgadmin/settings/templates/settings/settings.js +++ /dev/null @@ -1,59 +0,0 @@ -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 = $('
'); - $('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/tools/backup/templates/backup/js/backup.js b/web/pgadmin/tools/backup/templates/backup/js/backup.js deleted file mode 100644 index 361926da..00000000 --- a/web/pgadmin/tools/backup/templates/backup/js/backup.js +++ /dev/null @@ -1,812 +0,0 @@ -// 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([ - '', - '
', - '
', - ' ', - '
', - '
', - '<% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - '<% } %>' - ].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 = $("
"); - // 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 = $("
"); - 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/templates/datagrid/js/datagrid.js b/web/pgadmin/tools/datagrid/templates/datagrid/js/datagrid.js deleted file mode 100644 index ce952f0f..00000000 --- a/web/pgadmin/tools/datagrid/templates/datagrid/js/datagrid.js +++ /dev/null @@ -1,536 +0,0 @@ -define('tools.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 = '
'+ - '
'+ - '
'+ - ''+ - '
'+ - '
'; - // 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(''+grid_title+''); - 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(''+panel_title+''); - 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/templates/debugger/js/debugger.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js deleted file mode 100644 index acf37408..00000000 --- a/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js +++ /dev/null @@ -1,456 +0,0 @@ -define([ - 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'alertify', - 'pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror', - 'backform', '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/templates/debugger/js/debugger_ui.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js deleted file mode 100644 index 8c7076c5..00000000 --- a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js +++ /dev/null @@ -1,785 +0,0 @@ -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] = ""; - } - } - else { - if (arg_cnt) { - arg_cnt = arg_cnt - 1; - def_val_list[j] = default_args[arg_cnt] - } - else { - def_val_list[j] = ""; - } - } - } - - 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] != "") { - 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] != "") { - 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": ""}); - def_val_list[i] = ""; - } - } - 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] = ""; - } - } - 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] = ""; - } - } - } - - for (i = 0; i < argtype.length; i++) { - if (this.data['proargmodes'] == null) { - var use_def_value = false - if (def_val_list[i] != "") { - 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] != "") { - 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/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js deleted file mode 100644 index cfa2db9c..00000000 --- a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js +++ /dev/null @@ -1,1699 +0,0 @@ -define([ - 'babel-polyfill', 'sources/gettext', 'sources/url_for' ,'jquery', 'underscore', 'underscore.string', 'alertify', - 'pgadmin','pgadmin.browser', 'backbone', 'backgrid', 'sources/../bundle/codemirror', 'backform', - 'tools.debugger.ui', - 'sources/alerts/alertify_wrapper', - - 'wcdocker', 'pgadmin.backform', - 'pgadmin.backgrid' - -], function( - babelPolyfill, 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, '
') // Newlines with
- .replace(/()+/g, '
'); // multiple
with single
- } 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: '
' - }) - - // 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: '
' - }) - - // 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: '
' - }) - - // 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: '
' - }) - - // 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: '
' - }) - - // 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 = $('
'); - var code_editor_area = $('').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( - $('
', {'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/templates/grant_wizard/js/grant_wizard.js b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js deleted file mode 100644 index ee618b32..00000000 --- a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js +++ /dev/null @@ -1,1129 +0,0 @@ -// 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 = $("
"); - - 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("
"); - - // 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 = - $('
'); - - // 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 Finish 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/templates/import_export/js/import_export.js b/web/pgadmin/tools/import_export/templates/import_export/js/import_export.js deleted file mode 100644 index 429b5ed7..00000000 --- a/web/pgadmin/tools/import_export/templates/import_export/js/import_export.js +++ /dev/null @@ -1,542 +0,0 @@ -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 = $("
"), - 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/templates/maintenance/js/maintenance.js b/web/pgadmin/tools/maintenance/templates/maintenance/js/maintenance.js deleted file mode 100644 index 9b5dc29a..00000000 --- a/web/pgadmin/tools/maintenance/templates/maintenance/js/maintenance.js +++ /dev/null @@ -1,434 +0,0 @@ -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([ - '', - '
', - '
', - ' ', - '
', - '
', - '<% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - '<% } %>' - ].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([ - '', - '
', - ' <% for (var i=0; i < options.length; i++) { %>', - ' <% var option = options[i]; %>', - ' ', - ' <% } %>', - '
' - ].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 = $("
"); - - 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/templates/restore/js/restore.js b/web/pgadmin/tools/restore/templates/restore/js/restore.js deleted file mode 100644 index bacfcd3f..00000000 --- a/web/pgadmin/tools/restore/templates/restore/js/restore.js +++ /dev/null @@ -1,563 +0,0 @@ -// 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([ - '', - '
', - '
', - ' ', - '
', - '
', - '<% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - '<% } %>' - ].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 = $("
"); - 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/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js deleted file mode 100644 index a402ff4f..00000000 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js +++ /dev/null @@ -1,3797 +0,0 @@ -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', - 'misc.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; - var CodeMirror = codemirror.default; - - // 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, - 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 = $(''); - var output_container = $('
').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: '
' - }) - - var explain = new pgAdmin.Browser.Panel({ - name: 'explain', - title: gettext("Explain"), - width: '100%', - height:'100%', - isCloseable: false, - isPrivate: true, - content: '
' - }) - - var messages = new pgAdmin.Browser.Panel({ - name: 'messages', - title: gettext("Messages"), - width: '100%', - height:'100%', - isCloseable: false, - isPrivate: true, - content: '
' - }) - - var history = new pgAdmin.Browser.Panel({ - name: 'history', - title: gettext("History"), - width: '100%', - height:'100%', - isCloseable: false, - isPrivate: true, - content: '
' - }) - - // 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 = '
' - + gettext('The text has changed. Do you want to save changes?') - + '
'; - 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 + '
' + 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/user_management/templates/user_management/js/user_management.js b/web/pgadmin/tools/user_management/templates/user_management/js/user_management.js deleted file mode 100644 index 3fe41a5a..00000000 --- a/web/pgadmin/tools/user_management/templates/user_management/js/user_management.js +++ /dev/null @@ -1,642 +0,0 @@ -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([ - ''].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 = [ - '
', - ' ', - '
', - '
',].join("\n"), - headerTpl = _.template(header), - data = { - canAdd: true, - add_title: gettext('Add new user') - }, - $gridBody = $("
", { - 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 = $("
").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; - });