diff --git a/src/common.js b/src/common.js
index b705094f7e..a99fb74ddc 100644
--- a/src/common.js
+++ b/src/common.js
@@ -62,9 +62,65 @@
};
Sao.common.date_format = function() {
- // TODO
- // http://stackoverflow.com/questions/2678230/how-to-getting-browser-current-locale-preference-using-javascript
- return '%m/%d/%Y';
+ var context = Sao.Session.current_session.context;
+ if (context.locale && context.locale.date) {
+ return context.locale.date
+ .replace('%d', 'dd')
+ .replace('%j', 'oo')
+ .replace('%a', 'D')
+ .replace('%A', 'DD')
+ .replace('%m', 'mm')
+ .replace('%b', 'M')
+ .replace('%B', 'MM')
+ .replace('%y', 'y')
+ .replace('%Y', 'yy');
+ }
+ return jQuery.datepicker.W3C;
+ };
+
+ Sao.common.format_time = function(format, date) {
+ var pad = Sao.common.pad;
+ return format.replace('%H', pad(date.getHours(), 2))
+ .replace('%M', pad(date.getMinutes(), 2))
+ .replace('%S', pad(date.getSeconds(), 2));
+ };
+
+ Sao.common.parse_time = function(format, value) {
+ var getNumber = function(pattern) {
+ var i = format.indexOf(pattern);
+ if (~i) {
+ var number = parseInt(value.slice(i, i + pattern.length), 10);
+ if (!isNaN(number)) {
+ return number;
+ }
+ }
+ return 0;
+ };
+ return new Sao.Time(getNumber('%H'), getNumber('%M'), getNumber('%S'));
+ };
+
+ Sao.common.format_datetime = function(date_format, time_format, date) {
+ return (jQuery.datepicker.formatDate(date_format, date) + ' ' +
+ Sao.common.format_time(time_format, date));
+ };
+
+ Sao.common.parse_datetime = function(date_format, time_format, value) {
+ var date = jQuery.datepicker.parseDate(date_format, value);
+ var time_value = value.replace(jQuery.datepicker.formatDate(
+ date_format + ' ', date), '');
+ var time = Sao.common.parse_time(time_format, time_value);
+ date.setHours(time.getHours());
+ date.setMinutes(time.getMinutes());
+ date.setSeconds(time.getSeconds());
+ return date;
+ };
+
+ Sao.common.pad = function(number, length) {
+ var str = '' + number;
+ while (str.length < length) {
+ str = '0' + str;
+ }
+ return str;
};
Sao.common.text_to_float_time = function(text, conversion, digit) {
diff --git a/src/model.js b/src/model.js
index a505983db1..a7e5489729 100644
--- a/src/model.js
+++ b/src/model.js
@@ -1120,14 +1120,78 @@
});
Sao.field.DateTime = Sao.class_(Sao.field.Field, {
- _default: null
+ _default: null,
+ time_format: function(record) {
+ return record.expr_eval(this.description.format);
+ },
+ set_client: function(record, value, force_change) {
+ if (!(value instanceof Date)) {
+ try {
+ value = Sao.common.parse_datetime(
+ Sao.common.date_format(),
+ this.time_format(record),
+ value);
+ } catch (e) {
+ value = this._default;
+ }
+ }
+ Sao.field.DateTime._super.set_client.call(this, record, value,
+ force_change);
+ },
+ get_client: function(record) {
+ var value = Sao.field.Date._super.get_client.call(this, record);
+ if (value) {
+ return Sao.common.format_datetime(Sao.common.date_format(),
+ this.time_format(record), value);
+ }
+ return '';
+ }
});
Sao.field.Date = Sao.class_(Sao.field.Field, {
- _default: null
+ _default: null,
+ set_client: function(record, value, force_change) {
+ if (!(value instanceof Date)) {
+ try {
+ value = jQuery.datepicker.parseDate(
+ Sao.common.date_format(), value);
+ } catch (e) {
+ value = this._default;
+ }
+ }
+ Sao.field.Date._super.set_client.call(this, record, value,
+ force_change);
+ },
+ get_client: function(record) {
+ var value = Sao.field.Date._super.get_client.call(this, record);
+ if (value) {
+ return jQuery.datepicker.formatDate(Sao.common.date_format(),
+ value);
+ }
+ return '';
+ }
});
Sao.field.Time = Sao.class_(Sao.field.Field, {
+ _default: null,
+ time_format: function(record) {
+ return record.expr_eval(this.description.format);
+ },
+ set_client: function(record, value, force_change) {
+ if (!(value instanceof Sao.Time)) {
+ value = Sao.common.parse_time(this.time_format(record), value);
+ }
+ Sao.field.Time._super.set_client.call(this, record, value,
+ force_change);
+ },
+ get_client: function(record) {
+ var value = Sao.field.Time._super.get_client.call(this, record);
+ if (value) {
+ return Sao.common.format_time(this.time_format(record),
+ value);
+ }
+ return '';
+ }
});
Sao.field.Number = Sao.class_(Sao.field.Field, {
diff --git a/src/rpc.js b/src/rpc.js
index 3865d12af2..3e283e62f9 100644
--- a/src/rpc.js
+++ b/src/rpc.js
@@ -77,7 +77,9 @@
value.month - 1, value.day);
break;
case 'time':
- throw new Error('Time support not implemented');
+ value = new Sao.Time(value.hour, value.minute,
+ value.second);
+ break;
case 'buffer':
// javascript's atob does not understand linefeed
// characters
@@ -138,6 +140,13 @@
if (parent) {
parent[index] = value;
}
+ } else if (value instanceof Sao.Time) {
+ value = {
+ '__class__': 'time',
+ 'hour': value.getHours(),
+ 'minute': value.getMinutes(),
+ 'second': value.getSeconds()
+ };
} else if (value instanceof Sao.Decimal) {
value = {
'__class__': 'Decimal',
diff --git a/src/sao.js b/src/sao.js
index b35bc24b46..6f7099ce4a 100644
--- a/src/sao.js
+++ b/src/sao.js
@@ -35,6 +35,30 @@ var Sao = {};
Sao.Decimal = Number;
+ Sao.Time = Sao.class_(Object, {
+ init: function(hour, minute, second) {
+ this.date = new Date(0, 0, 0, hour, minute, second);
+ },
+ getHours: function() {
+ return this.date.getHours();
+ },
+ setHours: function(hour) {
+ this.date.setHours(hour);
+ },
+ getMinutes: function() {
+ return this.date.getMinutes();
+ },
+ setMinutes: function(minute) {
+ this.date.setMinutes(minute);
+ },
+ getSeconds: function() {
+ return this.date.getSeconds();
+ },
+ setSeconds: function(second) {
+ this.date.setSeconds(second);
+ }
+ });
+
Sao.config = {};
Sao.config.limit = 1000;
Sao.config.display_size = 20;
diff --git a/src/view.js b/src/view.js
index 555e87e3d8..81048b7562 100644
--- a/src/view.js
+++ b/src/view.js
@@ -34,6 +34,10 @@
return Sao.View.Tree.Many2OneColumn;
case 'date':
return Sao.View.Tree.DateColumn;
+ case 'datetime':
+ return Sao.View.Tree.DateTimeColumn;
+ case 'time':
+ return Sao.View.Tree.TimeColumn;
case 'one2many':
return Sao.View.Tree.One2ManyColumn;
case 'many2many':
@@ -478,6 +482,7 @@
});
Sao.View.Tree.CharColumn = Sao.class_(Object, {
+ class_: 'column-char',
init: function(model, attributes) {
this.type = 'field';
this.model = model;
@@ -488,7 +493,7 @@
},
get_cell: function() {
var cell = jQuery('');
- cell.addClass('column-char');
+ cell.addClass(this.class_);
return cell;
},
update_text: function(cell, record) {
@@ -512,33 +517,25 @@
});
Sao.View.Tree.IntegerColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-integer',
get_cell: function() {
var cell = Sao.View.Tree.IntegerColumn._super.get_cell.call(this);
cell.css('text-align', 'right');
- cell.removeClass('column-char');
- cell.addClass('column-integer');
return cell;
}
});
Sao.View.Tree.FloatColumn = Sao.class_(Sao.View.Tree.IntegerColumn, {
- init: function(model, attributes) {
- Sao.View.Tree.FloatColumn._super.init.call(this, model, attributes);
- },
- get_cell: function() {
- var cell = Sao.View.Tree.FloatColumn._super.get_cell.call(this);
- cell.removeClass('column-integer');
- cell.addClass('column-float');
- return cell;
- }
+ class_: 'column-float'
});
Sao.View.Tree.BooleanColumn = Sao.class_(Sao.View.Tree.IntegerColumn, {
+ class_: 'column-boolean',
get_cell: function() {
return jQuery('', {
'type': 'checkbox',
'disabled': true,
- 'class': 'column-boolean'
+ 'class': this.class_
});
},
update_text: function(cell, record) {
@@ -547,15 +544,11 @@
});
Sao.View.Tree.Many2OneColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- get_cell: function() {
- var cell = Sao.View.Tree.Many2OneColumn._super.get_cell.call(this);
- cell.removeClass('column-char');
- cell.addClass('column-many2one');
- return cell;
- }
+ class_: 'column-many2one'
});
Sao.View.Tree.SelectionColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-selection',
init: function(model, attributes) {
Sao.View.Tree.SelectionColumn._super.init.call(this, model,
attributes);
@@ -569,12 +562,6 @@
Sao.common.selection_mixin.update_selection.call(this, record,
this.field, callback);
},
- get_cell: function() {
- var cell = Sao.View.Tree.Many2OneColumn._super.get_cell.call(this);
- cell.removeClass('column-char');
- cell.addClass('column-selection');
- return cell;
- },
update_text: function(cell, record) {
this.update_selection(record, function() {
var value = this.field.get(record);
@@ -602,39 +589,30 @@
});
Sao.View.Tree.DateColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- init: function(model, attributes) {
- Sao.View.Tree.DateColumn._super.init.call(this, model, attributes);
- this.date_format = Sao.common.date_format();
- },
- update_text: function(cell, record) {
- var text;
- var value = this.field.get_client(record);
- var pad = function(txt) {
- return (txt.toString().length < 2) ? '0' + txt : txt;
- };
- if (value) {
- text = this.date_format.replace('%d', pad(value.getDate()));
- text = text.replace('%m', pad(value.getMonth() + 1));
- text = text.replace('%Y', value.getFullYear());
- text = text.replace('%y', value.getFullYear().toString()
- .substring(2, 4));
- } else {
- text = '';
- }
- cell.text(text);
- }
+ class_: 'column-date'
+ });
+
+ Sao.View.Tree.DateTimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-datetime'
+ });
+
+ Sao.View.Tree.TimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-time'
});
Sao.View.Tree.One2ManyColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-one2many',
update_text: function(cell, record) {
cell.text('( ' + this.field.get_client(record).length + ' )');
}
});
Sao.View.Tree.Many2ManyColumn = Sao.class_(Sao.View.Tree.One2ManyColumn, {
+ class_: 'column-many2many'
});
Sao.View.Tree.FloatTimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
+ class_: 'column-float_time',
init: function(model, attributes) {
Sao.View.Tree.FloatTimeColumn._super.init.call(this, model,
attributes);
@@ -1138,6 +1116,8 @@
return Sao.View.Form.Sha;
case 'date':
return Sao.View.Form.Date;
+ case 'datetime':
+ return Sao.View.Form.DateTime;
case 'integer':
case 'biginteger':
return Sao.View.Form.Integer;
@@ -1280,8 +1260,7 @@
}
},
set_value: function(record, field) {
- var value = this.el.val() || '';
- field.set_client(record, value);
+ field.set_client(record, this.el.val());
}
});
@@ -1308,33 +1287,74 @@
this.date.datepicker({
showOn: 'button'
});
+ this.date.change(this.focus_out.bind(this));
+ this._set_button();
+ },
+ _get_color_el: function() {
+ return this.date;
+ },
+ get_format: function(record, field) {
+ return Sao.common.date_format();
+ },
+ _set_button: function() {
this.date.next('button').text('').button({
icons: {
primary: 'ui-icon-calendar'
},
text: false
});
- this.date.change(this.focus_out.bind(this));
- },
- _get_color_el: function() {
- return this.date;
},
display: function(record, field) {
+ this.date.datepicker('option', 'dateFormat',
+ this.get_format(record, field));
+ // Button must be set after changing any option
+ this._set_button();
Sao.View.Form.Date._super.display.call(this, record, field);
- if (record) {
- var value = record.field_get_client(this.field_name);
- this.date.datepicker('setDate', value);
- } else {
- this.date.datepicker('setDate', null);
- }
+ this.date.val(record.field_get_client(this.field_name));
},
set_value: function(record, field) {
- var value = this.date.datepicker('getDate');
- field.set_client(record, value);
+ field.set_client(record, this.date.val());
}
});
- // TODO DateTime, Time
+ Sao.View.Form.DateTime = Sao.class_(Sao.View.Form.Date, {
+ init: function(field_name, model, attributes) {
+ Sao.View.Form.DateTime._super.init.call(this, field_name, model,
+ attributes);
+ this.date.datepicker('option', 'beforeShow', function() {
+ var time = ' ' + Sao.common.format_time(
+ this.field().time_format(this.record()),
+ this._get_time());
+ this.date.datepicker('option', 'dateFormat',
+ Sao.common.date_format() + time);
+ // Button must be set after changing any option
+ this._set_button();
+ this.date.prop('disabled', true);
+ }.bind(this));
+ this.date.datepicker('option', 'onClose', function() {
+ this.date.prop('disabled', false);
+ }.bind(this));
+ // Button must be set after changing any option
+ this._set_button();
+ },
+ _get_time: function() {
+ return Sao.common.parse_datetime(Sao.common.date_format(),
+ this.field().time_format(this.record()), this.date.val());
+ },
+ get_format: function(record, field) {
+ var time = '';
+ if (record) {
+ var value = record.field_get(this.field_name);
+ time = ' ' + Sao.common.format_time(field.time_format(record),
+ value);
+ }
+ return Sao.common.date_format() + time;
+ }
+ });
+
+ Sao.View.Form.Time = Sao.class_(Sao.View.Form.Char, {
+ class_: 'form-time'
+ });
Sao.View.Form.Integer = Sao.class_(Sao.View.Form.Char, {
class_: 'form-integer',
@@ -1344,8 +1364,7 @@
this.el.css('text-align', 'right');
},
set_value: function(record, field) {
- var value = Number(this.el.val());
- field.set_client(record, value);
+ field.set_client(record, this.el.val());
}
});