Introducing the infrastructure for colleciton of the subnodes listing,

creation, and editing within the properties panel infrastructure.

We do use the backgrid.js for listing the subnode collection, and for
editing/creating new object for the subnode, we do use the same
infrastructure using the backform.
This commit is contained in:
Khushboo Vashi 2015-10-28 22:36:09 +05:30 committed by Ashesh Vashi
parent c53b57a013
commit c1503ade47
16 changed files with 4530 additions and 644 deletions

View File

@ -15,5 +15,7 @@ Require.js 2.1.18 BSD/MIT http://requirejs.org/
Underscore.js 1.8.3 MIT http://underscorejs.org/
Underscore.string 387ab72d49 MIT http://epeli.github.io/underscore.string/
Backform.js 5859b4f9db MIT https://github.com/AmiliaApp/backform
Backbone 1.1.2 MIT http://backbonejs.org
font-Awesome 4.3 SIL OFL http://fortawesome.github.io/Font-Awesome/
font-mfizz 1.2 MIT http://fizzed.com/oss/font-mfizz
backgrid.js 0.3.5 MIT http://backgridjs.com/

View File

@ -32,8 +32,13 @@ function($, _, pgAdmin, Backbone) {
name: null
},
schema: [
{id: 'id', label: 'ID', type: 'int', group: null, mode: ['properties']},
{id: 'name', label:'Name', type: 'text', group: null, mode: ['properties', 'edit', 'create']}
{
id: 'id', label: 'ID', type: 'int', group: null,
mode: ['properties']
},{
id: 'name', label:'Name', type: 'text', group: null,
mode: ['properties', 'edit', 'create']
}
],
validate: function(attrs, options) {
if (!this.isNew() && 'id' in this.changed) {

View File

@ -52,8 +52,7 @@ OWNER TO helpdesk;\n';
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],
[{ data: d, browser: obj, item: i }]);
obj.Nodes[d._type], [i]);
}
}
};
@ -442,10 +441,8 @@ OWNER TO helpdesk;\n';
typeof obj.Nodes[d._type].callbacks[eventName] ==
'function') {
return obj.Nodes[d._type].callbacks[eventName].apply(
obj.Nodes[d._type], [{
data: d, browser: obj, item: item,
eventName: eventName, options: options
}]);
obj.Nodes[d._type], [item, eventName, options]
);
}
}
switch (eventName) {
@ -618,7 +615,8 @@ OWNER TO helpdesk;\n';
},
messages: {
'server_lost': '{{ _('Connection to the server has been lost!') }}',
'click_for_detailed_msg': '{{ _('%s<br><br>click here for details!') }}'
'click_for_detailed_msg': '{{ _('%s<br><br>click here for details!') }}',
'general_cateogty': '{{ _('General') }}'
}
});

View File

@ -46,38 +46,6 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
return child;
};
// Defines - which control needs to be instantiated in different modes.
// i.e. Node properties, create, edit, etc.
var controlType = {
'properties': {
'int': 'uneditable-input',
'text': 'uneditable-input',
'numeric': 'uneditable-input',
'date': 'date',
'boolean': 'bool-text',
'options': Backform.ReadonlyOptionControl,
'multiline': 'textarea'
},
'edit': {
'int': 'input',
'text': 'input',
'numeric': 'input',
'date': 'date',
'boolean': 'boolean',
'options': 'select',
'multiline': 'textarea'
},
'create': {
'int': 'input',
'text': 'input',
'numeric': 'input',
'date': 'date',
'boolean': 'boolean',
'options': 'select',
'multiline': 'textarea'
}
};
_.extend(pgAdmin.Browser.Node, {
// Node type
type: undefined,
@ -115,9 +83,9 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
//
// Used to generate view for the particular node properties, edit,
// creation.
getView: function(type, el, node, formType, callback) {
getView: function(type, el, node, formType, callback, data) {
if (!this.type || this.type == '' || !type in controlType)
if (!this.type || this.type == '')
// We have no information, how to generate view for this type.
return null;
@ -131,62 +99,25 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
// node.
return null;
var opts = {};
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') {
opts[this.model.idAttribute || 'id'] = node._id;
attrs[this.model.idAttribute || 'id'] = node._id;
}
// We know - which data model to be used for this object.
var newModel = new (this.model.extend({urlRoot: urlBase}))(opts);
var newModel = new (this.model.extend({urlRoot: urlBase}))(attrs, {
onChangeData: data,
onChangeCallback: callback
}),
groups = Backform.generateViewSchema(newModel, type);
// 'schema' has the information about how to generate the form.
if (newModel.schema && _.isArray(newModel.schema)) {
var groups = {};
_.each(newModel.schema, function(f) {
// Do we understand - what control, we're creating
// here?
if (f && f.mode && _.isObject(f.mode) &&
_.indexOf(f.mode, type) != -1 &&
type in controlType) {
// Each field is kept in specified group, or in
// 'General' category.
var group = f.group || '{{ _("General") }}';
// Generate the empty group list (if not exists)
if (!groups[group]) {
groups[group] = [];
}
// Temporarily store in dictionaly format for
// utilizing it later.
groups[group].push({
name: f.id, label: f.label,
control: controlType[type][f.type],
// Do we need to show this control in this mode?
visible: f.show && newModel[f.show] &&
typeof newModel[f.show] == "function" ?
newModel[f.show] : f.show,
// This can be disabled in some cases (if not hidden)
disabled: (type == 'properties' ? true : (
f.disabled && newModel[f.disabled] &&
typeof newModel[f.disabled] == "function" ?
newModel[f.disabled] : undefined)),
options: f.options
});
}
});
// Do we have fields to genreate controls, which we
// understand?
if (_.isEmpty(groups)) {
return null;
}
if (groups) {
var fields = [];
// This will contain the actual view
var view;
@ -214,34 +145,24 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
// This is definetely not in create mode
newModel.fetch()
.success(function(res, msg, xhr) {
if (res) {
// We got the latest attributes of the
// object. Render the view now.
view.render();
if (typeof(callback) != "undefined") {
callback(view);
}
}
// We got the latest attributes of the
// object. Render the view now.
view.render();
})
.error(function(m, jqxhr) {
.error(function(jqxhr, error, message) {
// TODO:: We may not want to continue from here
console.log(arguments);
Alertify.pgNotifier(
"error", jqxhr,
error, jqxhr,
S(
"{{ _("Error fetching the properties - %%s!") }}"
).sprintf(jqxhr.statusText).value()
).sprintf(message).value()
);
});
} else {
// Yay - render the view now!
view.render();
if (typeof(callback) != "undefined") {
callback(view);
}
}
}
return view;
}
@ -475,7 +396,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
null).show()
},
// Callback called - when a node is selected in browser tree.
selected: function(o) {
selected: function(item) {
// Show the information about the selected node in the below panels,
// which are visible at this time:
// + Properties
@ -483,53 +404,54 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
// + Dependents
// + Dependencies
// + Statistics
var b = pgBrowser,
t = b.tree,
d = t.itemData(item);
// Update the menu items
pgAdmin.Browser.enable_disable_menus.apply(o.browser, [o.item]);
pgAdmin.Browser.enable_disable_menus.apply(b, [item]);
if (o && o.data && o.browser) {
var br = o.browser;
if ('properties' in br.panels &&
br.panels['properties'] &&
br.panels['properties'].panel &&
br.panels['properties'].panel.isVisible()) {
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(o.item, o.data,
pgBrowser.panels['properties'].panel);
this.showProperties(item, d, b.panels['properties'].panel);
}
if ('sql' in br.panels &&
br.panels['sql'] &&
br.panels['sql'].panel &&
br.panels['sql'].panel.isVisible()) {
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 br.panels &&
br.panels['statistics'] &&
br.panels['statistics'].panel &&
br.panels['statistics'].panel.isVisible()) {
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 br.panels &&
br.panels['dependencies'] &&
br.panels['dependencies'].panel &&
br.panels['dependencies'].panel.isVisible()) {
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 br.panels &&
br.panels['dependents'] &&
br.panels['dependents'].panel &&
br.panels['dependents'].panel.isVisible()) {
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.)
}
}
},
refresh_node: function(args) {
this.callbacks.selected();
refresh_node: function(item) {
this.callbacks.selected(undefined, item);
}
},
/**********************************************************************
@ -544,7 +466,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
tree = pgAdmin.Browser.tree,
j = panel.$container.find('.obj_properties').first(),
view = j.data('obj-view'),
content = $('<div></div>')
content = $('<div tabindex="1"></div>')
.addClass('pg-prop-content col-xs-12'),
// Template function to create the button-group
createButtons = function(buttons, extraClasses) {
@ -644,9 +566,27 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
}
// Make sure the HTML element is empty.
j.empty();
// Create a view to edit/create the properties in fieldsets
view = that.getView(action, content, data, 'dialog');
var modelChanged = function(m, o) {
var btnGroup = o.find('.pg-prop-btn-group'),
btnSave = btnGroup.find('button[type="save"]'),
btnReset = btnGroup.find('button[type="reset"]');
if (m.sessChanged()) {
btnSave.prop('disabled', false);
btnSave.removeAttr('disabled');
btnReset.prop('disabled', false);
btnReset.removeAttr('disabled');
} else {
btnSave.prop('disabled', true);
btnSave.attr('disabled', 'disabled');
btnReset.prop('disabled', true);
btnReset.attr('disabled', 'disabled');
}
};
// Create a view to edit/create the properties in fieldsets
view = that.getView(action, content, data, 'dialog', modelChanged, j);
if (view) {
// Save it to release it later
j.data('obj-view', view);
@ -659,14 +599,13 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
register: function(btn) {
// Save the changes
btn.click(function() {
var m = view.model,
c = m.isNew() ? m.attributes :
m.changedAttributes();
d = m.toJSON(true);
if (c && !_.isEmpty(c)) {
m.save({} ,{
attrs: m.attributes,
if (d && !_.isEmpty(d)) {
m.save({}, {
attrs: d,
validate: false,
success: function() {
onSaveFunc.call();
},
@ -858,14 +797,70 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
return args[arg];
});
},
Collection: Backbone.Collection.extend({
}),
// Base class for Node Model
Model: Backbone.Model.extend({
parse: function(res) {
var self = this;
if ('node' in res && res['node']) {
this.tnode = _.extend({}, res.node);
self.tnode = _.extend({}, res.node);
delete res.node;
}
if (self.schema && _.isArray(self.schema)) {
_.each(self.schema, function(s) {
if (s.id in res) {
var o;
switch(s.type) {
case 'collection':
o = self.get(s.id)
o.reset(res[s.id], [{silent: true}]);
res[s.id] = o;
break;
case 'model':
o = self.get(s.id);
o.set(res[s.id], [{silent: true}]);
res[s.id] = o;
break;
default:
break;
}
}
});
}
return res;
},
initialize: function(attributes, options) {
var self = this;
if (this.schema && _.isArray(this.schema)) {
_.each(this.schema, function(s) {
var obj = null;
switch(s.type) {
case 'collection':
if (_.isString(s.model) &&
s.model in pgBrowser.Nodes) {
var node = pgBrowser.Nodes[s.model];
obj = new (node.Collection)(null, {model: node.model});
} else {
obj = new (pgBrowser.Node.Collection)(null, {model: s.model});
}
break;
case 'model':
if (_.isString(s.model) &&
s.model in pgBrowser.Nodes[s.model]) {
obj = new (pgBrowser.Nodes[s.model].Model)(null);
} else {
obj = new (s.model)(null);
}
break;
default:
return;
}
obj.name = s.id;
self.set(s.id, obj, {silent: true});
});
}
}
})
});

View File

@ -0,0 +1,240 @@
/*
backgrid
http://github.com/wyuenho/backgrid
Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
Licensed under the MIT license.
*/
.backgrid-container {
position: relative;
display: block;
width: 100%;
height: 465px;
padding: 0;
overflow: auto;
border: 0;
}
.backgrid {
width: 100%;
max-width: 100%;
background-color: transparent;
border-collapse: collapse;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.backgrid th,
.backgrid td {
display: none;
height: 20px;
max-width: 250px;
padding: 4px 5px;
overflow: hidden;
line-height: 20px;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
border-bottom: 1px solid #DDD;
}
.backgrid th.renderable,
.backgrid td.renderable {
display: table-cell;
}
.backgrid th {
font-weight: bold;
text-align: center;
}
.backgrid th.sortable a {
text-decoration: none;
white-space: nowrap;
cursor: pointer;
}
.backgrid thead th {
vertical-align: bottom;
background-color: #f9f9f9;
}
.backgrid thead th a {
display: block;
}
.backgrid.backgrid-striped tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
.backgrid tbody tr.empty {
font-style: italic;
color: gray;
}
.backgrid tbody tr.empty td {
display: inherit;
text-align: center;
}
.backgrid td.editor {
padding: 0;
}
.backgrid td.editor,
.backgrid tbody tr:nth-child(odd) td.editor {
background-color: rgba(82, 168, 236, 0.1);
outline: 1px solid rgba(82, 168, 236, 0.8);
outline-offset: -1px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition-duration: 200ms;
-moz-transition-duration: 200ms;
-o-transition-duration: 200ms;
transition-duration: 200ms;
-webkit-transition-property: width, outline, background-color;
-moz-transition-property: width, outline, background-color;
-o-transition-property: width, outline, background-color;
transition-property: width, outline, background-color;
-webkit-transition-timing-function: ease-in-out;
-moz-transition-timing-function: ease-in-out;
-o-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
}
.backgrid td.editor input[type=text] {
display: block;
width: 100%;
height: 100%;
padding: 0 5px;
margin: 0;
background-color: transparent;
border: 0;
outline: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
}
.backgrid td.editor input[type=text]::-ms-clear {
display: none;
}
.backgrid td.error,
.backgrid tbody tr:nth-child(odd) td.error {
background-color: rgba(255, 210, 77, 0.1);
outline: 1px solid #ffd24d;
}
.backgrid td.editor :focus,
.backgrid th.editor :focus {
outline: 0;
}
.backgrid .sort-caret {
display: inline-block;
width: 0;
height: 0;
margin-left: 0.3em;
border: 0;
content: "";
}
.backgrid .ascending .sort-caret {
vertical-align: baseline;
border-top: none;
border-right: 4px solid transparent;
border-bottom: 4px solid #000000;
border-left: 4px solid transparent;
}
.backgrid .descending .sort-caret {
vertical-align: super;
border-top: 4px solid #000000;
border-right: 4px solid transparent;
border-bottom: none;
border-left: 4px solid transparent;
}
.backgrid .string-cell,
.backgrid .uri-cell,
.backgrid .email-cell,
.backgrid .string-cell.editor input[type=text],
.backgrid .uri-cell.editor input[type=text],
.backgrid .email-cell.editor input[type=text] {
text-align: left;
}
.backgrid .date-cell,
.backgrid .time-cell,
.backgrid .datetime-cell,
.backgrid .number-cell,
.backgrid .integer-cell,
.backgrid .percent-cell,
.backgrid .date-cell.editor input[type=text],
.backgrid .time-cell.editor input[type=text],
.backgrid .datetime-cell.editor input[type=text],
.backgrid .number-cell.editor input[type=text],
.backgrid .integer-cell.editor input[type=text],
.backgrid .percent-cell.editor input[type=text] {
text-align: right;
}
.backgrid .boolean-cell,
.backgrid .boolean-cell.editor input[type=checkbox] {
text-align: center;
}
.backgrid .select-cell {
text-align: center;
}
.backgrid .select-cell.editor {
padding: 0;
}
.backgrid .select-cell.editor select {
display: block;
width: 100%;
height: 28px;
padding: 4px 5px;
margin: 0;
line-height: 28px;
vertical-align: middle;
background-color: white;
border: 0;
outline: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.backgrid .select-cell.editor select[multiple] {
height: auto;
}
.backgrid .select-cell.editor :focus {
border: 0;
outline: 0;
}
.backgrid .select-cell.editor select::-moz-focus-inner,
.backgrid .select-cell.editor optgroup::-moz-focus-inner,
.backgrid .select-cell.editor option::-moz-focus-inner,
.backgrid .select-cell.editor select::-o-focus-inner,
.backgrid .select-cell.editor optgroup::-o-focus-inner,
.backgrid .select-cell.editor option::-o-focus-inner {
border: 0;
}

View File

@ -0,0 +1 @@
.backgrid-container{position:relative;display:block;width:100%;height:465px;padding:0;overflow:auto;border:0}.backgrid{width:100%;max-width:100%;background-color:transparent;border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.backgrid th,.backgrid td{display:none;height:20px;max-width:250px;padding:4px 5px;overflow:hidden;line-height:20px;text-align:left;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;border-bottom:1px solid #DDD}.backgrid th.renderable,.backgrid td.renderable{display:table-cell}.backgrid th{font-weight:bold;text-align:center}.backgrid th.sortable a{text-decoration:none;white-space:nowrap;cursor:pointer}.backgrid thead th{vertical-align:bottom;background-color:#f9f9f9}.backgrid thead th a{display:block}.backgrid.backgrid-striped tbody tr:nth-child(even){background-color:#f9f9f9}.backgrid tbody tr.empty{font-style:italic;color:gray}.backgrid tbody tr.empty td{display:inherit;text-align:center}.backgrid td.editor{padding:0}.backgrid td.editor,.backgrid tbody tr:nth-child(odd) td.editor{background-color:rgba(82,168,236,0.1);outline:1px solid rgba(82,168,236,0.8);outline-offset:-1px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition-duration:200ms;-moz-transition-duration:200ms;-o-transition-duration:200ms;transition-duration:200ms;-webkit-transition-property:width,outline,background-color;-moz-transition-property:width,outline,background-color;-o-transition-property:width,outline,background-color;transition-property:width,outline,background-color;-webkit-transition-timing-function:ease-in-out;-moz-transition-timing-function:ease-in-out;-o-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out}.backgrid td.editor input[type=text]{display:block;width:100%;height:100%;padding:0 5px;margin:0;background-color:transparent;border:0;outline:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none}.backgrid td.editor input[type=text]::-ms-clear{display:none}.backgrid td.error,.backgrid tbody tr:nth-child(odd) td.error{background-color:rgba(255,210,77,0.1);outline:1px solid #ffd24d}.backgrid td.editor :focus,.backgrid th.editor:focus{outline:0}.backgrid .sort-caret{display:inline-block;width:0;height:0;margin-left:.3em;border:0;content:""}.backgrid .ascending .sort-caret{vertical-align:baseline;border-top:0;border-right:4px solid transparent;border-bottom:4px solid #000;border-left:4px solid transparent}.backgrid .descending .sort-caret{vertical-align:super;border-top:4px solid #000;border-right:4px solid transparent;border-bottom:0;border-left:4px solid transparent}.backgrid .string-cell,.backgrid .uri-cell,.backgrid .email-cell,.backgrid .string-cell.editor input[type=text],.backgrid .uri-cell.editor input[type=text],.backgrid .email-cell.editor input[type=text]{text-align:left}.backgrid .date-cell,.backgrid .time-cell,.backgrid .datetime-cell,.backgrid .number-cell,.backgrid .integer-cell,.backgrid .percent-cell,.backgrid .date-cell.editor input[type=text],.backgrid .time-cell.editor input[type=text],.backgrid .datetime-cell.editor input[type=text],.backgrid .number-cell.editor input[type=text],.backgrid .integer-cell.editor input[type=text],.backgrid .percent-cell.editor input[type=text]{text-align:right}.backgrid .boolean-cell,.backgrid .boolean-cell.editor input[type=checkbox]{text-align:center}.backgrid .select-cell{text-align:center}.backgrid .select-cell.editor{padding:0}.backgrid .select-cell.editor select{display:block;width:100%;height:28px;padding:4px 5px;margin:0;line-height:28px;vertical-align:middle;background-color:white;border:0;outline:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.backgrid .select-cell.editor select[multiple]{height:auto}.backgrid .select-cell.editor :focus{border:0;outline:0}.backgrid .select-cell.editor select::-moz-focus-inner,.backgrid .select-cell.editor optgroup::-moz-focus-inner,.backgrid .select-cell.editor option::-moz-focus-inner,.backgrid .select-cell.editor select::-o-focus-inner,.backgrid .select-cell.editor optgroup::-o-focus-inner,.backgrid .select-cell.editor option::-o-focus-inner{border:0}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -382,7 +382,7 @@ iframe {
}
.pg-prop-btn-group button:not(:first-child):not(:last-child) {
margin: 0px;
margin-left: 0px;
}
.pg-prop-content {
@ -448,3 +448,116 @@ fieldset[disabled] .form-control {
margin-left: -18px;
margin-right: 9px;
}
/* Sub-Node */
.table-bordered > thead > tr > td, .table-bordered > thead > tr > th {
border-bottom-width: 1px;
}
.subnode > table.backgrid{
width: 99%;
margin: 0.1% 0.49%;
padding: 0;
}
.backgrid thead th{
background-color: #2c76b4;
}
.backgrid th, .backgrid td {
line-height: 18px;
font-size: 12px;
}
.backgrid td {
padding-top: 0px;
padding-bottom: 0px;
padding-left: 2px;
padding-right: 2px;
}
.backgrid thead th a {
color: #ffffff;
}
.backgrid > th.object {
width: 30px;
}
.edit-cell, .delete-cell {
text-align:center !important;
width:25px;
}
.backgrid td.renderable:not(.editable):not(.delete-cell) {
background-color: #eee;
}
.subnode-header {
background-color:#2c76b4;
height:35px;
color:#FFFFFF;
border-radius:5px 5px 0px 0px;
padding-top:3px;
}
.subnode-header > button.add {
float:right;
margin-right:15px;
}
.subnode {
margin-top:5px;
padding-top:0px;
border-left:1px solid #dadada;
border-bottom:1px solid #ddd;
border-right:1px solid #ddd;
}
.subnode-dialog {
bottom: 0;
left: 0;
overflow-x: auto;
overflow-y: auto;
right: 0;
height:auto;
margin-left: 23px;
background-color: #dadada;
margin-top: 0px;
border: 1px solid #a9a9a9;
}
.subnode-body {
height:auto;
overflow:inherit;
}
.subnode-footer {
height:38px;;
margin: 0px, 15px;
min-height:40px;
vertical-align: bottom;
}
.sub-node-form {
height:auto;
padding: 1px;
}
.sub-node-form > .nav-tabs {
background-color: #DADADA;
}
.sub-node-form > ul.tab-content{
background-color: #FFFFFF;
padding-left: 15px;
left: 1px;
}
table.backgrid tr.new {
background-color: rgba(82, 168, 236, 0.1) !important;
box-sizing: border-box;
outline: 1px solid rgba(82, 168, 236, 0.8);
outline-offset: -1px;
}

View File

@ -10,10 +10,11 @@
// Set up Backform appropriately for the environment. Start with AMD.
if (typeof define === 'function' && define.amd) {
define(['underscore', 'jquery', 'backbone', 'backform'], function(_, $, Backbone, Backform) {
define(['underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'pgadmin.backgrid'],
function(_, $, Backbone, Backform, Backgrid) {
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backform.
return factory(root, _, $, Backbone, Backform);
return factory(root, _, $, Backbone, Backform, Backgrid);
});
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
@ -21,14 +22,16 @@
var _ = require('underscore') || root._,
$ = root.jQuery || root.$ || root.Zepto || root.ender,
Backbone = require('backbone') || root.Backbone,
Backform = require('backform') || root.Backform;
factory(root, _, $, Backbone, Backform);
Backform = require('backform') || root.Backform,
Backgrid = require('backgrid') || root.Backgrid;
pgAdminBackgrid = require('pgadmin.backgrid');
factory(root, _, $, Backbone, Backform, Backgrid);
// Finally, as a browser global.
} else {
factory(root, root._, (root.jQuery || root.Zepto || root.ender || root.$), root.Backbone, root.Backform);
factory(root, root._, (root.jQuery || root.Zepto || root.ender || root.$), root.Backbone, root.Backform, root.Backgrid);
}
}(this, function(root, _, $, Backbone, Backform) {
}(this, function(root, _, $, Backbone, Backform, Backgrid) {
// HTML markup global class names. More can be added by individual controls
// using _.extend. Look at RadioControl as an example.
@ -41,6 +44,51 @@
setGroupContentClassName: "fieldset-content col-xs-12"
});
var controlMapper = Backform.controlMapper = {
'int': ['uneditable-input', 'input', 'integer'],
'text': ['uneditable-input', 'input', 'string'],
'numeric': ['uneditable-input', 'input', 'number'],
'date': 'datepicker',
'boolean': 'boolean',
'options': ['readonly-option', 'select', Backgrid.Extension.PGSelectCell],
'multiline': ['textarea', 'textarea', 'string'],
'collection': ['sub-node-collection', 'sub-node-collection', 'string']
};
var getMappedControl = Backform.getMappedControl = function(type, mode) {
if (type in Backform.controlMapper) {
var m = Backform.controlMapper[type];
if (!_.isArray(m)) {
return m;
}
var idx = 1, len = _.size(m);
switch (mode) {
case 'properties':
idx = 0;
break;
case 'edit':
case 'create':
case 'control':
idx = 1;
break;
case 'cell':
idx = 2;
break;
default:
idx = 0;
break;
}
return m[idx > len ? 0 : idx];
}
alert ("Developer: did you forget to put/implement the control type - '" + type + "' in mapper");
return null;
}
// Override the Backform.Control to allow to track changes in dependencies,
// and rerender the View element
var BackformControlInit = Backform.Control.prototype.initialize;
@ -72,7 +120,7 @@
'<div class="<%=Backform.controlsClassName%>">',
'<% for (var i=0; i < options.length; i++) { %>',
' <% var option = options[i]; %>',
' <% if (option.value === rawValue) { %>',
' <% if (option.value === rawValue) { %>',
' <span class="<%=Backform.controlClassName%> uneditable-input" disabled><%-option.label%></span>',
' <% } %>',
'<% } %>',
@ -214,5 +262,196 @@
events: {}
});
var SubNodeCollectionControl = Backform.SubNodeCollectionControl = Backform.Control.extend({
render: function() {
var field = _.defaults(this.field.toJSON(), this.defaults),
attributes = this.model.toJSON(),
attrArr = field.name.split('.'),
name = attrArr.shift(),
path = attrArr.join('.'),
rawValue = this.keyPathAccessor(attributes[name], path),
data = _.extend(field, {
rawValue: rawValue,
value: this.formatter.fromRaw(rawValue, this.model),
attributes: attributes,
formatter: this.formatter
}),
evalF = function(f, m) {
return (_.isFunction(f) ? !!f(m) : !!f);
};
// Evaluate the disabled, visible, required, canAdd, cannEdit & canDelete option
_.extend(data, {
disabled: evalF(data.disabled, this.model),
visible: evalF(data.visible, this.model),
required: evalF(data.required, this.model),
canAdd: evalF(data.canAdd, this.model),
canEdit: evalF(data.canEdit, this.model),
canDelete: evalF(data.canDelete, this.model)
});
// Show Backgrid Control
grid = (data.subnode == undefined) ? "" : this.showGridControl(data);
this.$el.html(grid).addClass(field.name);
this.updateInvalid();
return this;
},
showGridControl: function(data) {
var gridHeader = ["<div class='subnode-header'>",
" <label class='control-label col-sm-4'>" + data.label + "</label>" ,
" <button class='btn-sm btn-default add'>Add</buttton>",
"</div>"].join("\n");
gridBody = $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode' >").append(gridHeader);
var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype,
columns = [],
gridColumns = [],
groups = Backform.generateViewSchema(subnode, this.field.get('mode')),
schema = [];
// Prepare columns for backgrid
_.each(groups, function(fields, key) {
_.each(fields, function(f) {
if (!f.control && !f.cell) {
return;
}
f.cel_priority = _.indexOf(data.columns, f.name);
if (f.cel_priority != -1) {
columns.push(f);
}
});
schema.push({label: key, fields: fields});
});
// Set visibility of Add button
if (data.disabled || data.canAdd == false) {
$(gridBody).find("button.add").remove();
}
// Insert Delete Cell into Grid
if (data.disabled == false && data.canDelete) {
columns.unshift({
name: "pg-backform-delete", label: "",
cell: Backgrid.Extension.DeleteCell,
editable: false, priority: -1
});
}
// Insert Edit Cell into Grid
if (data.disabled == false && data.canEdit) {
var editCell = Backgrid.Extension.ObjectCell.extend({
schema: schema
});
columns.unshift({
name: "pg-backform-edit", label: "", cell : editCell,
priority: -2
});
}
var collections = this.model.get(data.name);
// Initialize a new Grid instance
var grid = new Backgrid.Grid({
columns: _.sortBy(columns, function(c) { return c.cell_priority; }),
collection: collections,
className: "backgrid table-bordered"
});
// Render subNode grid
subNodeGrid = grid.render().$el;
// Combine Edit and Delete Cell
if (data.canDelete && data.canEdit) {
$(subNodeGrid).find("th.pg-backform-delete").remove();
$(subNodeGrid).find("th.pg-backform-edit").attr("colspan", "2");
}
$dialog = gridBody.append(subNodeGrid);
// Add button callback
$dialog.find('button.add').click(function(e) {
e.preventDefault();
grid.insertRow({});
newRow = $(grid.body.rows[collections.length - 1].$el);
newRow.attr("class", "new").click(function(e) {
$(this).attr("class", "");
});
return false;
});
return $dialog;
}
});
///////
// Generate a schema (as group members) based on the model's schema
//
// It will be used by the grid, properties, and dialog view generation
// functions.
var generateViewSchema = Backform.generateViewSchema = function(Model, mode) {
var proto = (Model && Model.prototype) || Model,
schema = (proto && proto.schema),
groups, pgBrowser = window.pgAdmin.Browser;
// 'schema' has the information about how to generate the form.
if (schema && _.isArray(schema)) {
var evalASFunc = evalASFunc = function(prop) {
return ((prop && proto[prop] &&
typeof proto[prop] == "function") ? proto[prop] : prop);
};
groups = {};
_.each(schema, function(s) {
// Do we understand - what control, we're creating
// here?
if (!s.mode || (s && s.mode && _.isObject(s.mode) &&
_.indexOf(s.mode, mode) != -1)) {
// Each field is kept in specified group, or in
// 'General' category.
var group = s.group || pgBrowser.messages.general_cateogty,
control = Backform.getMappedControl(s.type, mode),
cell = s.cell || Backform.getMappedControl(s.type, 'cell');
if (control == null) {
return;
}
// Generate the empty group list (if not exists)
groups[group] = (groups[group] || []);
var o = _.extend(_.clone(s), {
name: s.id,
// Do we need to show this control in this mode?
visible: evalASFunc(s.show),
// This can be disabled in some cases (if not hidden)
disabled: (mode == 'properties' ? true : evalASFunc(s.disabled)),
subnode: (_.isString(s.model) && s.model in pgBrowser.Nodes) ?
pgBrowser.Nodes[s.model].model : s.model,
canAdd: (mode == 'properties' ? false : evalASFunc(s.canAdd)),
canEdit: (mode == 'properties' ? false : evalASFunc(s.canEdit)),
canDelete: (mode == 'properties' ? false : evalASFunc(s.canDelete)),
mode: mode,
control: control,
cell: cell
});
delete o.id;
// Temporarily store in dictionaly format for
// utilizing it later.
groups[group].push(o);
}
});
// Do we have fields to genreate controls, which we
// understand?
if (_.isEmpty(groups)) {
return null;
}
}
return groups;
}
return Backform;
}));

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,234 @@
(function(root, factory) {
// Set up Backform appropriately for the environment. Start with AMD.
if (typeof define === 'function' && define.amd) {
define(['underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'alertify'],
function(_, $, Backbone, Backform, Backgrid, Alertify) {
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backform.
return factory(root, _, $, Backbone, Backform, Alertify);
});
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
} else if (typeof exports !== 'undefined') {
var _ = require('underscore') || root._,
$ = root.jQuery || root.$ || root.Zepto || root.ender,
Backbone = require('backbone') || root.Backbone,
Backform = require('backform') || root.Backform;
Alertify = require('alertify') || root.Alertify;
factory(root, _, $, Backbone, Backform, Alertify);
// Finally, as a browser global.
} else {
factory(root, root._, (root.jQuery || root.Zepto || root.ender || root.$), root.Backbone, root.Backform);
}
} (this, function(root, _, $, Backbone, Backform, Alertify) {
var ObjectCellEditor = Backgrid.Extension.ObjectCellEditor = Backgrid.CellEditor.extend({
modalTemplate: _.template([
'<div class="subnode-dialog">',
' <div class="subnode-body"></div>',
' <div class="subnode-footer">',
' <button style ="float:right;margin-right:15px;margin-top: 4px;" class="cancel btn btn-danger" type="cancel">Cancel</button>',
' <button style ="float:right;margin-right:10px;margin-top: 4px;" class="save btn btn-primary" type="save">Save</button>',
' </div>',
'</div>'
].join("\n")),
stringTemplate: _.template([
'<div class="form-group">',
' <label class="control-label col-sm-4"><%=label%></label>',
' <div class="col-sm-8">',
' <input type="text" class="form-control" name="<%=name%>" value="<%=value%>" placeholder="<%=placeholder%>" />',
' </div>',
'</div>'
].join("\n")),
extendWithOptions: function(options) {
_.extend(this, options);
},
render: function () {
return this;
},
postRender: function(model, column) {
var editor = this,
el = this.el;
columns_length = this.columns_length;
if (column != null && column.get("name") != this.column.get("name"))
return false;
if (!_.isArray(this.schema)) throw new TypeError("schema must be an array");
// Create a Backbone model from our object if it does not exist
if (!this.origModel) {
this.origModel = this.model;
this.model = this.origModel.clone();
}
var $dialog = this.createDialog(columns_length);
// Add the Bootstrap form
var $form = $('<form class="form-dialog"></form>');
$dialog.find('div.subnode-body').append($form);
// Call Backform to prepare dialog
back_el = $dialog.find('form.form-dialog');
Backform.tabClassName = "sub-node-form col-sm-12";
objectView = new Backform.Dialog({
el: back_el, model: this.model, schema: this.schema,
});
objectView.render();
return this;
},
createDialog: function(noofcol) {
var editor1 = this,
$dialog = this.$dialog = $(this.modalTemplate({title: ""})),
tr = $("<tr>"),
td = $("<td>", {class: 'editable sortable renderable', style: 'height: auto', colspan: noofcol+2}).appendTo(tr);
noofcol = noofcol || 1;
// Handle close and save events
$dialog.find('button.cancel').click(function(e) {
e.preventDefault();
editor1.cancel();
tr.remove();
return false;
});
$dialog.find('button.save').click(function(e) {
e.preventDefault();
editor1.save();
tr.remove();
return false;
});
// Show the Bootstrap modal dialog
td.append($dialog.css('display', 'block'));
this.el.parent('tr').after(tr);
return $dialog;
},
save: function(options) {
options || (options = {});
var model = this.origModel,
column = this.column,
objectModel = this.model,
$form = this.$dialog.find('form');
// Retrieve values from the form, and store inside the object model
var changes = {};
_.each(this.schema, function(field) {
inputType = (field.control == 'datepicker' ? 'input' : field.control);
val = $form.find(inputType + '[name='+field.name+']').first().val()
val = (field.cell == 'integer') ? parseInt(val) :
(field.cell == 'number') ? parseFloat(val) : val
changes[field.name] = val;
});
objectModel.set(changes);
model.set(changes, options);
model.trigger("backgrid:edited", model, column, new Backgrid.Command({keyCode:13}));
return this;
},
cancel: function() {
this.origModel.trigger("backgrid:edited", this.origModel, this.column, new Backgrid.Command({keyCode:27}));
return this;
},
remove: function() {
this.$dialog.modal("hide").remove();
Backgrid.CellEditor.prototype.remove.apply(this, arguments);
return this;
}
});
var PGSelectCell = Backgrid.Extension.PGSelectCell = Backgrid.SelectCell.extend({
// It's possible to render an option group or use a
// function to provide option values too.
optionValues: function() {
var res = [];
opts = _.result(this.column.attributes, 'options');
_.each(opts, function(o) {
res.push([o.label, o.value]);
});
return res;
}
});
var ObjectCell = Backgrid.Extension.ObjectCell = Backgrid.Cell.extend({
editorOptionDefaults: {
schema: []
},
className: "edit-cell",
editor: ObjectCellEditor,
initialize: function(options) {
Backgrid.Cell.prototype.initialize.apply(this, arguments);
// Pass on cell options to the editor
var cell = this,
editorOptions = {};
_.each(this.editorOptionDefaults, function(def, opt) {
if (!cell[opt]) cell[opt] = def;
if (options && options[opt]) cell[opt] = options[opt];
editorOptions[opt] = cell[opt];
});
editorOptions['el'] = $(this.el);
editorOptions['columns_length'] = this.column.collection.length
this.listenTo(this.model, "backgrid:edit", function (model, column, cell, editor) {
if (column.get("name") == this.column.get("name"))
editor.extendWithOptions(editorOptions);
});
},
enterEditMode: function () {
var $content = this.$el.html();
Backgrid.Cell.prototype.enterEditMode.apply(this, arguments);
var editable = Backgrid.callByNeed(this.column.editable(), this.column, this.model);
if (editable) this.$el.html("<i class='fa fa-minus-square-o'></i>");
},
render: function(){
this.$el.empty();
this.$el.html("<i class='fa fa-pencil-square-o'></i>");
this.delegateEvents();
return this;
}
});
var DeleteCell = Backgrid.Extension.DeleteCell = Backgrid.Cell.extend({
/** @property */
className: "delete-cell",
events: {
"click": "deleteRow"
},
deleteRow: function (e) {
e.preventDefault();
that = this;
Alertify.confirm(
'Delete Row',
'Are You Sure, you want to delete this object?',
function(evt) {
that.model.collection.remove(that.model);
},
function(evt) {
return true;
}
);
},
initialize: function () {
Backgrid.Cell.prototype.initialize.apply(this, arguments);
},
render: function () {
this.$el.empty();
this.$el.html("<i class='fa fa-trash'></i>");
this.delegateEvents();
return this;
}
});
return Backgrid;
}));

View File

@ -1,10 +1,18 @@
/*!
* Datepicker for Bootstrap v1.5.0-dev (https://github.com/eternicode/bootstrap-datepicker)
* Datepicker for Bootstrap v1.5.0 (https://github.com/eternicode/bootstrap-datepicker)
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/(function($, undefined){
*/(function(factory){
if (typeof define === "function" && define.amd) {
define(["jquery"], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(jQuery);
}
}(function($, undefined){
function UTCDate(){
return new Date(Date.UTC.apply(Date, arguments));
@ -25,6 +33,9 @@
return this[method].apply(this, arguments);
};
}
function isValidDate(d) {
return d && !isNaN(d.getTime());
}
var DateArray = (function(){
var extras = {
@ -115,6 +126,7 @@
this.setStartDate(this._o.startDate);
this.setEndDate(this._o.endDate);
this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
this.setDaysOfWeekHighlighted(this.o.daysOfWeekHighlighted);
this.setDatesDisabled(this.o.datesDisabled);
this.fillDow();
@ -175,6 +187,20 @@
o.minViewMode = 0;
}
switch (o.maxViewMode) {
case 0:
case 'days':
o.maxViewMode = 0;
break;
case 1:
case 'months':
o.maxViewMode = 1;
break;
default:
o.maxViewMode = 2;
}
o.startView = Math.min(o.startView, o.maxViewMode);
o.startView = Math.max(o.startView, o.minViewMode);
// true, false, or Number > 0
@ -219,6 +245,13 @@
return parseInt(d, 10);
});
o.daysOfWeekHighlighted = o.daysOfWeekHighlighted||[];
if (!$.isArray(o.daysOfWeekHighlighted))
o.daysOfWeekHighlighted = o.daysOfWeekHighlighted.split(/[,\s]*/);
o.daysOfWeekHighlighted = $.map(o.daysOfWeekHighlighted, function(d){
return parseInt(d, 10);
});
o.datesDisabled = o.datesDisabled||[];
if (!$.isArray(o.datesDisabled)) {
var datesDisabled = [];
@ -269,6 +302,7 @@
o.defaultViewDate = UTCToday();
}
o.showOnFocus = o.showOnFocus !== undefined ? o.showOnFocus : true;
o.zIndexOffset = o.zIndexOffset !== undefined ? o.zIndexOffset : 10;
},
_events: [],
_secondaryEvents: [],
@ -376,9 +410,10 @@
this.element.is(e.target) ||
this.element.find(e.target).length ||
this.picker.is(e.target) ||
this.picker.find(e.target).length
this.picker.find(e.target).length ||
this.picker.hasClass('datepicker-inline')
)){
$(this.picker).hide();
this.hide();
}
}, this)
}]
@ -534,7 +569,7 @@
}
if (element) {
element.val('').change();
element.val('');
}
this.update();
@ -567,11 +602,11 @@
var formatted = this.getFormattedDate();
if (!this.isInput){
if (this.component){
this.element.find('input').val(formatted).change();
this.element.find('input').val(formatted);
}
}
else {
this.element.val(formatted).change();
this.element.val(formatted);
}
return this;
},
@ -607,6 +642,12 @@
return this;
},
setDaysOfWeekHighlighted: function(daysOfWeekHighlighted){
this._process_options({daysOfWeekHighlighted: daysOfWeekHighlighted});
this.update();
return this;
},
setDatesDisabled: function(datesDisabled){
this._process_options({datesDisabled: datesDisabled});
this.update();
@ -619,17 +660,17 @@
var calendarWidth = this.picker.outerWidth(),
calendarHeight = this.picker.outerHeight(),
visualPadding = 10,
windowWidth = $(this.o.container).width(),
windowHeight = $(this.o.container).height(),
scrollTop = $(this.o.container).scrollTop(),
appendOffset = $(this.o.container).offset();
container = $(this.o.container),
windowWidth = container.width(),
scrollTop = container.scrollTop(),
appendOffset = container.offset();
var parentsZindex = [];
this.element.parents().each(function(){
var itemZIndex = $(this).css('z-index');
if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex));
});
var zIndex = Math.max.apply(Math, parentsZindex) + 10;
var zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset;
var offset = this.component ? this.component.parent().offset() : this.element.offset();
var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
@ -666,20 +707,17 @@
// auto y orientation is best-situation: top or bottom, no fudging,
// decision based on which shows more of the calendar
var yorient = this.o.orientation.y,
top_overflow, bottom_overflow;
top_overflow;
if (yorient === 'auto'){
top_overflow = -scrollTop + top - calendarHeight;
bottom_overflow = scrollTop + windowHeight - (top + height + calendarHeight);
if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
yorient = 'top';
else
yorient = 'bottom';
yorient = top_overflow < 0 ? 'bottom' : 'top';
}
this.picker.addClass('datepicker-orient-' + yorient);
if (yorient === 'top')
top += height;
else
top -= calendarHeight + parseInt(this.picker.css('padding-top'));
else
top += height;
if (this.o.rtl) {
var right = windowWidth - (left + width);
@ -743,6 +781,8 @@
this.viewDate = new Date(this.o.startDate);
else if (this.viewDate > this.o.endDate)
this.viewDate = new Date(this.o.endDate);
else
this.viewDate = this.o.defaultViewDate;
if (fromArgs){
// setting date by clicking
@ -757,6 +797,7 @@
this._trigger('clearDate');
this.fill();
this.element.change();
return this;
},
@ -764,12 +805,11 @@
var dowCnt = this.o.weekStart,
html = '<tr>';
if (this.o.calendarWeeks){
this.picker.find('.datepicker-days thead tr:first-child .datepicker-switch')
this.picker.find('.datepicker-days .datepicker-switch')
.attr('colspan', function(i, val){
return parseInt(val) + 1;
});
var cell = '<th class="cw">&#160;</th>';
html += cell;
html += '<th class="cw">&#160;</th>';
}
while (dowCnt < this.o.weekStart + 7){
html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
@ -823,6 +863,9 @@
$.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
cls.push('disabled');
}
if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1){
cls.push('highlighted');
}
if (this.o.datesDisabled.length > 0 &&
$.grep(this.o.datesDisabled, function(d){
return isUTCEquals(date, d); }).length > 0) {
@ -836,6 +879,12 @@
if ($.inArray(date.valueOf(), this.range) !== -1){
cls.push('selected');
}
if (date.valueOf() === this.range[0]){
cls.push('range-start');
}
if (date.valueOf() === this.range[this.range.length-1]){
cls.push('range-end');
}
}
return cls;
},
@ -850,17 +899,21 @@
endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
todaytxt = dates[this.o.language].today || dates['en'].today || '',
cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat,
tooltip;
if (isNaN(year) || isNaN(month))
return;
this.picker.find('.datepicker-days thead .datepicker-switch')
.text(dates[this.o.language].months[month]+' '+year);
.text(DPGlobal.formatDate(new UTCDate(year, month), titleFormat, this.o.language));
this.picker.find('tfoot .today')
.text(todaytxt)
.toggle(this.o.todayBtn !== false);
this.picker.find('tfoot .clear')
.text(cleartxt)
.toggle(this.o.clearBtn !== false);
this.picker.find('thead .datepicker-title')
.text(this.o.title)
.toggle(this.o.title !== '');
this.updateNavArrows();
this.fillMonths();
var prevMonth = UTCDate(year, month-1, 28),
@ -868,6 +921,9 @@
prevMonth.setUTCDate(day);
prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
var nextMonth = new Date(prevMonth);
if (prevMonth.getUTCFullYear() < 100){
nextMonth.setUTCFullYear(prevMonth.getUTCFullYear());
}
nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
nextMonth = nextMonth.valueOf();
var html = [];
@ -921,8 +977,8 @@
this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
var months = this.picker.find('.datepicker-months')
.find('th:eq(1)')
.text(year)
.find('.datepicker-switch')
.text(this.o.maxViewMode < 2 ? 'Months' : year)
.end()
.find('span').removeClass('active');
@ -956,7 +1012,7 @@
html = '';
year = parseInt(year/10, 10) * 10;
var yearCont = this.picker.find('.datepicker-years')
.find('th:eq(1)')
.find('.datepicker-switch')
.text(year + '-' + (year + 9))
.end()
.find('td');
@ -967,6 +1023,8 @@
classes;
for (var i = -1; i < 11; i++){
classes = ['year'];
tooltip = null;
if (i === -1)
classes.push('old');
else if (i === 10)
@ -975,7 +1033,24 @@
classes.push('active');
if (year < startYear || year > endYear)
classes.push('disabled');
html += '<span class="' + classes.join(' ') + '">' + year + '</span>';
if (this.o.beforeShowYear !== $.noop) {
var yrBefore = this.o.beforeShowYear(new Date(year, 0, 1));
if (yrBefore === undefined)
yrBefore = {};
else if (typeof(yrBefore) === 'boolean')
yrBefore = {enabled: yrBefore};
else if (typeof(yrBefore) === 'string')
yrBefore = {classes: yrBefore};
if (yrBefore.enabled === false)
classes.push('disabled');
if (yrBefore.classes)
classes = classes.concat(yrBefore.classes.split(/\s+/));
if (yrBefore.tooltip)
tooltip = yrBefore.tooltip;
}
html += '<span class="' + classes.join(' ') + '"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>' + year + '</span>';
year += 1;
}
yearCont.html(html);
@ -1005,13 +1080,13 @@
break;
case 1:
case 2:
if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){
if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() || this.o.maxViewMode < 2){
this.picker.find('.prev').css({visibility: 'hidden'});
}
else {
this.picker.find('.prev').css({visibility: 'visible'});
}
if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){
if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() || this.o.maxViewMode < 2){
this.picker.find('.next').css({visibility: 'hidden'});
}
else {
@ -1023,6 +1098,7 @@
click: function(e){
e.preventDefault();
e.stopPropagation();
var target = $(e.target).closest('span, td, th'),
year, month, day;
if (target.length === 1){
@ -1176,8 +1252,8 @@
},
moveMonth: function(date, dir){
if (!date)
return undefined;
if (!isValidDate(date))
return this.o.defaultViewDate;
if (!dir)
return date;
var new_date = new Date(date.valueOf()),
@ -1235,8 +1311,10 @@
keydown: function(e){
if (!this.picker.is(':visible')){
if (e.keyCode === 40 || e.keyCode === 27) // allow down to re-show picker
if (e.keyCode === 40 || e.keyCode === 27) { // allow down to re-show picker
this.show();
e.stopPropagation();
}
return;
}
var dateChanged = false,
@ -1252,6 +1330,7 @@
else
this.hide();
e.preventDefault();
e.stopPropagation();
break;
case 37: // left
case 39: // right
@ -1314,6 +1393,9 @@
// As such, its behavior should not be hijacked.
break;
case 13: // enter
if (!this.o.forceParse) {
break;
}
focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
if (this.o.keyboardNavigation) {
this._toggle_multidate(focusDate);
@ -1361,13 +1443,13 @@
showMode: function(dir){
if (dir){
this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
this.viewMode = Math.max(this.o.minViewMode, Math.min(this.o.maxViewMode, this.viewMode + dir));
}
this.picker
.children('div')
.hide()
.filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName)
.css('display', 'block');
.show();
this.updateNavArrows();
}
};
@ -1410,8 +1492,13 @@
return;
this.updating = true;
var dp = $(e.target).data('datepicker'),
new_date = dp.getUTCDate(),
var dp = $(e.target).data('datepicker');
if (typeof(dp) === "undefined") {
return;
}
var new_date = dp.getUTCDate(),
i = $.inArray(e.target, this.inputs),
j = i - 1,
k = i + 1,
@ -1509,14 +1596,20 @@
}
if (typeof option === 'string' && typeof data[option] === 'function'){
internal_return = data[option].apply(data, args);
if (internal_return !== undefined)
return false;
}
});
if (internal_return !== undefined)
return internal_return;
else
if (
internal_return === undefined ||
internal_return instanceof Datepicker ||
internal_return instanceof DateRangePicker
)
return this;
if (this.length > 1)
throw new Error('Using only allowed for the collection of a single element (' + option + ' function)');
else
return internal_return;
};
$.fn.datepicker = datepickerPlugin;
@ -1524,10 +1617,12 @@
autoclose: false,
beforeShowDay: $.noop,
beforeShowMonth: $.noop,
beforeShowYear: $.noop,
calendarWeeks: false,
clearBtn: false,
toggleActive: false,
daysOfWeekDisabled: [],
daysOfWeekHighlighted: [],
datesDisabled: [],
endDate: Infinity,
forceParse: true,
@ -1535,6 +1630,7 @@
keyboardNavigation: true,
language: 'en',
minViewMode: 0,
maxViewMode: 2,
multidate: false,
multidateSeparator: ',',
orientation: "auto",
@ -1547,7 +1643,8 @@
disableTouchKeyboard: false,
enableOnReadonly: true,
container: 'body',
immediateUpdates: false
immediateUpdates: false,
title: ''
};
var locale_opts = $.fn.datepicker.locale_opts = [
'format',
@ -1563,7 +1660,8 @@
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
today: "Today",
clear: "Clear"
clear: "Clear",
titleFormat: "MM yyyy"
}
};
@ -1593,7 +1691,9 @@
validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
parseFormat: function(format){
// IE treats \0 as a string end in inputs (truncating the value),
if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function')
return format;
// IE treats \0 as a string end in inputs (truncating the value),
// so it's a bad format delimiter, anyway
var separators = format.replace(this.validParts, '\0').split('\0'),
parts = format.match(this.validParts);
@ -1609,7 +1709,9 @@
return date;
if (typeof format === 'string')
format = DPGlobal.parseFormat(format);
var part_re = /([\-+]\d+)([dmwy])/,
if (format.toValue)
return format.toValue(date, format, language);
var part_re = /([\-+]\d+)([dmwy])/,
parts = date.match(/([\-+]\d+)([dmwy])/g),
part, dir, i;
if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){
@ -1714,7 +1816,9 @@
return '';
if (typeof format === 'string')
format = DPGlobal.parseFormat(format);
var val = {
if (format.toDisplay)
return format.toDisplay(date, format, language);
var val = {
d: date.getUTCDate(),
D: dates[language].daysShort[date.getUTCDay()],
DD: dates[language].days[date.getUTCDay()],
@ -1736,6 +1840,9 @@
return date.join('');
},
headTemplate: '<thead>'+
'<tr>'+
'<th colspan="7" class="datepicker-title"></th>'+
'</tr>'+
'<tr>'+
'<th class="prev">&#171;</th>'+
'<th colspan="5" class="datepicker-switch"></th>'+
@ -1789,7 +1896,7 @@
/* DATEPICKER VERSION
* =================== */
$.fn.datepicker.version = "1.4.1-dev";
$.fn.datepicker.version = '1.5.0';
/* DATEPICKER DATA-API
* ================== */
@ -1810,4 +1917,4 @@
datepickerPlugin.call($('[data-provide="datepicker-inline"]'));
});
}(window.jQuery));
}));

File diff suppressed because one or more lines are too long

View File

@ -22,6 +22,7 @@
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/font-awesome.css' if config.DEBUG else 'css/font-awesome.min.css') }}"/>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/font-mfizz.css') }}"/>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-datepicker3.css')}}"/>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/backgrid/backgrid.css')}}"/>
<!-- View specified stylesheets -->
{% for stylesheet in current_app.stylesheets %}
@ -44,9 +45,19 @@
"bootstrap": {
"deps": ['jquery'],
},
"backgrid": {
"deps": ['backform'],
"exports": 'Backgrid',
},
"bootstrap.datepicker": {
"deps": ['jquery', 'bootstrap'],
"exports": 'jQuery.fn.datepicker'
},
"pgadmin.backgrid": {
"deps": ["backgrid", "bootstrap.datepicker"],
},
"pgadmin.backform": {
"deps": ['backform', "pgadmin.backgrid"],
}{% for script in current_app.javascripts %}{% if 'deps' in script or 'exports' in script %},
'{{ script.name }}': {
{% if 'deps' in script %}"deps": [ {% set comma = False %}{% for dep in script['deps'] %} {% if comma %},{% else %}{% set comma = True %}{% endif %} '{{ dep }}'{% endfor %}],{% endif %}
@ -65,6 +76,8 @@
backbone: "{{ url_for('static', filename='js/' + ('backbone' if config.DEBUG else 'backbone-min')) }}",
"bootstrap.datepicker": "{{ url_for('static', filename='js/' + ('bootstrap-datepicker' if config.DEBUG else 'bootstrap-datepicker.min')) }}",
backform: "{{ url_for('static', filename='js/backform') }}",
backgrid: "{{ url_for('static', filename='js/backgrid/' + ('backgrid' if config.DEBUG else 'backgrid.min')) }}",
"pgadmin.backgrid": "{{ url_for('static', filename='js/backgrid/backgrid.pgadmin') }}",
'pgadmin.backform': "{{ url_for('static', filename='js/backform.pgadmin') }}"{% for script in current_app.javascripts %},
'{{ script.name }}': "{{ script.path }}"{% endfor %}
}