2012-04-17 12:09:49 +00:00
openerp . web . form = function ( instance ) {
var _t = instance . web . _t ,
_lt = instance . web . _lt ;
var QWeb = instance . web . qweb ;
2011-08-18 17:43:22 +00:00
2012-04-09 23:36:15 +00:00
/** @namespace */
2012-04-17 12:09:49 +00:00
instance . web . form = { } ;
2011-03-30 14:00:48 +00:00
2012-05-02 09:15:48 +00:00
/ * *
* Interface implemented by the form view or any other object
* able to provide the features necessary for the fields to work .
2012-07-12 12:56:37 +00:00
*
2012-05-02 09:15:48 +00:00
* Properties :
* - display _invalid _fields : if true , all fields where is _valid ( ) return true should
* be displayed as invalid .
* Events :
* - view _content _has _changed : when the values of the fields have changed . When
* this event is triggered all fields should reprocess their modifiers .
* /
2012-05-02 09:18:47 +00:00
instance . web . form . FieldManagerMixin = {
2012-05-02 09:15:48 +00:00
/ * *
* Must return the asked field as in fields _get .
* /
get _field : function ( field _name ) { } ,
/ * *
* Returns true when the view is in create mode .
* /
is _create _mode : function ( ) { } ,
} ;
2012-04-17 12:09:49 +00:00
instance . web . views . add ( 'form' , 'instance.web.FormView' ) ;
2012-07-25 14:13:33 +00:00
/ * *
* Properties :
* - actual _mode : always "view" , "edit" or "create" . Read - only property . Determines
2012-07-26 10:27:19 +00:00
* the mode used by the view .
2012-07-25 14:13:33 +00:00
* /
2012-06-21 23:56:41 +00:00
instance . web . FormView = instance . web . View . extend ( instance . web . form . FieldManagerMixin , {
2011-04-01 10:45:00 +00:00
/ * *
* Indicates that this view is not searchable , and thus that no search
* view should be displayed ( if there is one active ) .
* /
searchable : false ,
2012-03-12 15:34:52 +00:00
template : "FormView" ,
2011-12-16 13:00:00 +00:00
display _name : _lt ( 'Form' ) ,
2012-04-05 16:03:15 +00:00
view _type : "form" ,
2011-04-01 10:45:00 +00:00
/ * *
2012-04-17 12:09:49 +00:00
* @ constructs instance . web . FormView
* @ extends instance . web . View
2011-10-19 12:50:34 +00:00
*
2012-04-17 12:09:49 +00:00
* @ param { instance . web . Session } session the current openerp session
* @ param { instance . web . DataSet } dataset the dataset this view will work with
2011-04-01 10:45:00 +00:00
* @ param { String } view _id the identifier of the OpenERP view object
2011-12-21 17:16:02 +00:00
* @ param { Object } options
* - resize _textareas : [ true | false | max _height ]
2011-06-03 09:43:02 +00:00
*
2012-04-17 12:09:49 +00:00
* @ property { instance . web . Registry } registry = instance . web . form . widgets widgets registry for this form view instance
2011-04-01 10:45:00 +00:00
* /
2011-09-14 14:30:28 +00:00
init : function ( parent , dataset , view _id , options ) {
2012-07-25 14:18:32 +00:00
var self = this ;
2011-09-14 14:30:28 +00:00
this . _super ( parent ) ;
2011-08-08 17:32:30 +00:00
this . set _default _options ( options ) ;
2011-03-30 14:00:48 +00:00
this . dataset = dataset ;
this . model = dataset . model ;
2011-09-20 10:08:23 +00:00
this . view _id = view _id || false ;
2011-04-04 15:06:19 +00:00
this . fields _view = { } ;
2011-03-30 14:00:48 +00:00
this . fields = { } ;
2011-12-01 15:50:29 +00:00
this . fields _order = [ ] ;
2011-04-05 15:14:40 +00:00
this . datarecord = { } ;
2011-06-20 18:25:49 +00:00
this . default _focus _field = null ;
this . default _focus _button = null ;
2012-04-17 12:09:49 +00:00
this . fields _registry = instance . web . form . widgets ;
this . tags _registry = instance . web . form . tags ;
2011-06-17 14:19:45 +00:00
this . has _been _loaded = $ . Deferred ( ) ;
2011-08-24 15:13:57 +00:00
this . translatable _fields = [ ] ;
2011-12-05 13:50:37 +00:00
_ . defaults ( this . options , {
2012-04-10 10:07:41 +00:00
"not_interactible_on_create" : false ,
"initial_mode" : "view" ,
2012-08-01 16:12:04 +00:00
"disable_autofocus" : false ,
2011-12-05 13:50:37 +00:00
} ) ;
2012-01-10 14:41:13 +00:00
this . is _initialized = $ . Deferred ( ) ;
this . mutating _mutex = new $ . Mutex ( ) ;
2012-01-10 14:44:20 +00:00
this . on _change _mutex = new $ . Mutex ( ) ;
this . reload _mutex = new $ . Mutex ( ) ;
2012-06-20 11:15:27 +00:00
this . _ _clicked _inside = false ;
this . _ _blur _timeout = null ;
2012-07-20 14:13:04 +00:00
this . rendering _engine = new instance . web . form . FormRenderingEngine ( this ) ;
2012-04-16 14:45:42 +00:00
this . qweb = null ; // A QWeb instance will be created if the view is a QWeb template
2012-07-26 09:52:31 +00:00
self . set ( { actual _mode : self . options . initial _mode } ) ;
2012-07-25 14:18:32 +00:00
this . has _been _loaded . then ( function ( ) {
2012-07-25 14:31:50 +00:00
self . on ( "change:actual_mode" , self , self . check _actual _mode ) ;
2012-07-26 09:52:31 +00:00
self . check _actual _mode ( ) ;
2012-07-26 11:57:42 +00:00
self . on ( "change:actual_mode" , self , self . init _pager ) ;
self . init _pager ( ) ;
2012-07-25 14:18:32 +00:00
} ) ;
2012-09-06 10:25:26 +00:00
this . on ( 'clear_uncommitted_changes' , this , function ( e ) {
2012-09-05 12:35:32 +00:00
if ( ! this . can _be _discarded ( ) ) {
e . preventDefault ( ) ;
}
} ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-02-21 16:29:12 +00:00
destroy : function ( ) {
2012-03-19 17:03:36 +00:00
_ . each ( this . get _widgets ( ) , function ( w ) {
2012-06-25 14:40:13 +00:00
w . off ( 'focused blurred' ) ;
2012-02-21 16:29:12 +00:00
w . destroy ( ) ;
2011-07-11 16:15:46 +00:00
} ) ;
2012-08-24 18:27:07 +00:00
if ( this . $el ) {
this . $el . off ( '.formBlur' ) ;
2012-07-26 14:44:20 +00:00
}
2011-09-28 15:16:13 +00:00
this . _super ( ) ;
2011-07-11 16:15:46 +00:00
} ,
2011-03-30 14:00:48 +00:00
on _loaded : function ( data ) {
var self = this ;
2012-02-07 12:05:39 +00:00
if ( ! data ) {
2012-03-12 15:34:52 +00:00
throw new Error ( "No data provided." ) ;
2012-02-07 13:01:25 +00:00
}
2012-03-12 15:34:52 +00:00
if ( this . arch ) {
throw "Form view does not support multiple calls to on_loaded" ;
2011-09-20 11:21:47 +00:00
}
2012-02-07 12:05:39 +00:00
this . fields _order = [ ] ;
this . fields _view = data ;
2012-04-10 12:21:10 +00:00
this . rendering _engine . set _fields _registry ( this . fields _registry ) ;
this . rendering _engine . set _tags _registry ( this . tags _registry ) ;
2012-04-16 14:45:42 +00:00
if ( ! this . extract _qweb _template ( data ) ) {
this . rendering _engine . set _fields _view ( data ) ;
2012-08-24 18:27:07 +00:00
var $dest = this . $el . hasClass ( "oe_form_container" ) ? this . $el : this . $el . find ( '.oe_form_container' ) ;
2012-04-16 14:45:42 +00:00
this . rendering _engine . render _to ( $dest ) ;
}
2012-03-12 15:34:52 +00:00
2012-08-24 18:27:07 +00:00
this . $el . on ( 'mousedown.formBlur' , function ( ) {
2012-06-20 11:15:27 +00:00
self . _ _clicked _inside = true ;
} ) ;
2012-04-09 18:44:45 +00:00
2012-04-10 13:55:37 +00:00
this . $buttons = $ ( QWeb . render ( "FormView.buttons" , { 'widget' : self } ) ) ;
2012-04-10 07:59:14 +00:00
if ( this . options . $buttons ) {
this . $buttons . appendTo ( this . options . $buttons ) ;
} else {
2012-08-24 18:27:07 +00:00
this . $el . find ( '.oe_form_buttons' ) . replaceWith ( this . $buttons ) ;
2012-04-10 07:59:14 +00:00
}
2012-04-19 20:44:18 +00:00
this . $buttons . on ( 'click' , '.oe_form_button_create' , this . on _button _create ) ;
this . $buttons . on ( 'click' , '.oe_form_button_edit' , this . on _button _edit ) ;
this . $buttons . on ( 'click' , '.oe_form_button_save' , this . on _button _save ) ;
this . $buttons . on ( 'click' , '.oe_form_button_cancel' , this . on _button _cancel ) ;
2012-04-09 20:16:47 +00:00
2012-08-24 18:27:07 +00:00
this . $sidebar = this . options . $sidebar || this . $el . find ( '.oe_form_sidebar' ) ;
2012-04-18 14:24:39 +00:00
if ( ! this . sidebar && this . options . $sidebar ) {
2012-04-17 12:09:49 +00:00
this . sidebar = new instance . web . Sidebar ( this ) ;
2012-04-09 18:44:45 +00:00
this . sidebar . appendTo ( this . $sidebar ) ;
2012-08-31 15:37:58 +00:00
if ( this . fields _view . toolbar ) {
2012-04-17 13:32:42 +00:00
this . sidebar . add _toolbar ( this . fields _view . toolbar ) ;
}
2012-09-04 15:18:24 +00:00
this . sidebar . add _items ( 'other' , _ . compact ( [
self . is _action _enabled ( 'delete' ) && { label : _t ( 'Delete' ) , callback : self . on _button _delete } ,
self . is _action _enabled ( 'create' ) && { label : _t ( 'Duplicate' ) , callback : self . on _button _duplicate } ,
2012-08-06 07:58:22 +00:00
{ label : _t ( 'Set Default' ) , callback : function ( item ) { self . open _defaults _dialog ( ) ; } }
2012-09-04 15:18:24 +00:00
] ) ) ;
2011-07-26 21:00:05 +00:00
}
2012-07-26 00:23:18 +00:00
2012-08-08 07:38:21 +00:00
this . has _been _loaded . resolve ( ) ;
2012-07-26 00:23:18 +00:00
// Add bounce effect on button 'Edit' when click on readonly page view.
2012-08-24 18:27:07 +00:00
this . $el . find ( ".oe_form_field,label" ) . on ( 'click' , function ( e ) {
2012-07-26 10:14:22 +00:00
if ( self . get ( "actual_mode" ) == "view" ) {
2012-07-26 00:23:18 +00:00
var $button = self . options . $buttons . find ( ".oe_form_button_edit" ) ;
2012-08-08 19:38:42 +00:00
$button . effect ( 'bounce' , { distance : 18 , times : 5 } , 150 )
2012-07-26 00:23:18 +00:00
}
} ) ;
2012-04-05 16:03:15 +00:00
return $ . when ( ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-04-16 14:45:42 +00:00
extract _qweb _template : function ( fvg ) {
for ( var i = 0 , ii = fvg . arch . children . length ; i < ii ; i ++ ) {
var child = fvg . arch . children [ i ] ;
if ( child . tag === "templates" ) {
this . qweb = new QWeb2 . Engine ( ) ;
2012-04-17 12:09:49 +00:00
this . qweb . add _template ( instance . web . json _node _to _xml ( child ) ) ;
2012-04-16 14:45:42 +00:00
if ( ! this . qweb . has _template ( 'form' ) ) {
throw new Error ( "No QWeb template found for form view" ) ;
}
return true ;
}
}
this . qweb = null ;
return false ;
} ,
get _fvg _from _qweb : function ( record ) {
var view = this . qweb . render ( 'form' , this . get _qweb _context ( record ) ) ;
var fvg = _ . clone ( this . fields _view ) ;
2012-04-17 12:09:49 +00:00
fvg . arch = instance . web . xml _to _json ( instance . web . str _to _xml ( view ) . firstChild ) ;
2012-04-16 14:45:42 +00:00
return fvg ;
} ,
get _qweb _context : function ( record ) {
var self = this ,
new _record = { } ;
2012-04-17 14:32:27 +00:00
_ . each ( record , function ( value _ , name ) {
2012-04-16 14:45:42 +00:00
var r = _ . clone ( self . fields _view . fields [ name ] || { } ) ;
2012-04-17 14:32:27 +00:00
if ( ( r . type === 'date' || r . type === 'datetime' ) && value _ ) {
r . raw _value = instance . web . auto _str _to _date ( value _ ) ;
2012-04-16 14:45:42 +00:00
} else {
2012-04-17 14:32:27 +00:00
r . raw _value = value _ ;
2012-04-16 14:45:42 +00:00
}
2012-04-17 14:32:27 +00:00
r . value = instance . web . format _value ( value _ , r ) ;
2012-04-16 14:45:42 +00:00
new _record [ name ] = r ;
} ) ;
return {
record : new _record ,
new _record : ! record . id
} ;
} ,
kill _current _form : function ( ) {
_ . each ( this . getChildren ( ) , function ( el ) {
el . destroy ( ) ;
} ) ;
this . fields = { } ;
this . fields _order = [ ] ;
this . default _focus _field = null ;
this . default _focus _button = null ;
this . translatable _fields = [ ] ;
2012-08-24 18:27:07 +00:00
this . $el . find ( '.oe_form_container' ) . empty ( ) ;
2011-03-30 14:00:48 +00:00
} ,
2011-12-15 14:29:39 +00:00
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
widgetFocused : function ( ) {
2012-06-20 11:15:27 +00:00
// Clear click flag if used to focus a widget
this . _ _clicked _inside = false ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
if ( this . _ _blur _timeout ) {
clearTimeout ( this . _ _blur _timeout ) ;
2012-06-20 11:15:27 +00:00
this . _ _blur _timeout = null ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
}
} ,
widgetBlurred : function ( ) {
2012-06-20 11:15:27 +00:00
if ( this . _ _clicked _inside ) {
// clicked in an other section of the form (than the currently
// focused widget) => just ignore the blurring entirely?
this . _ _clicked _inside = false ;
return ;
}
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
var self = this ;
// clear timeout, if any
this . widgetFocused ( ) ;
this . _ _blur _timeout = setTimeout ( function ( ) {
2012-06-25 14:40:13 +00:00
self . trigger ( 'blurred' ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
} , 0 ) ;
} ,
2012-02-01 15:44:42 +00:00
do _load _state : function ( state , warm ) {
2011-12-15 14:29:39 +00:00
if ( state . id && this . datarecord . id != state . id ) {
2012-02-01 15:44:42 +00:00
if ( ! this . dataset . get _id _index ( state . id ) ) {
2012-01-25 08:53:10 +00:00
this . dataset . ids . push ( state . id ) ;
2011-12-15 14:29:39 +00:00
}
2012-02-01 15:44:42 +00:00
this . dataset . select _id ( state . id ) ;
if ( warm ) {
this . do _show ( ) ;
}
2011-12-15 14:29:39 +00:00
}
2011-10-26 14:42:18 +00:00
} ,
2012-06-27 10:02:06 +00:00
/ * *
*
* @ param { Object } [ options ]
2012-08-16 09:38:18 +00:00
* @ param { Boolean } [ mode = undefined ] If specified , switch the form to specified mode . Can be "edit" or "view" .
2012-06-27 10:02:06 +00:00
* @ param { Boolean } [ reload = true ] whether the form should reload its content on show , or use the currently loaded record
* @ return { $ . Deferred }
* /
2012-06-07 16:08:01 +00:00
do _show : function ( options ) {
2012-01-04 17:01:18 +00:00
var self = this ;
2012-06-07 16:08:01 +00:00
options = options || { } ;
2012-04-09 19:35:09 +00:00
if ( this . sidebar ) {
2012-08-24 18:27:07 +00:00
this . sidebar . $el . show ( ) ;
2012-04-09 19:35:09 +00:00
}
2012-04-09 19:43:33 +00:00
if ( this . $buttons ) {
2012-04-10 07:59:14 +00:00
this . $buttons . show ( ) ;
2012-04-09 19:43:33 +00:00
}
if ( this . $pager ) {
2012-04-10 11:26:56 +00:00
this . $pager . show ( ) ;
2012-04-09 19:43:33 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . show ( ) . css ( {
2012-08-01 16:12:04 +00:00
opacity : '0' ,
filter : 'alpha(opacity = 0)'
} ) ;
2012-08-24 18:27:07 +00:00
this . $el . add ( this . $buttons ) . removeClass ( 'oe_form_dirty' ) ;
2012-06-27 10:02:06 +00:00
var shown = this . has _been _loaded ;
if ( options . reload !== false ) {
shown = shown . pipe ( function ( ) {
if ( self . dataset . index === null ) {
// null index means we should start a new record
return self . on _button _new ( ) ;
2012-06-07 16:08:01 +00:00
}
2012-07-31 14:03:09 +00:00
var fields = _ . keys ( self . fields _view . fields ) ;
fields . push ( 'display_name' ) ;
return self . dataset . read _index ( fields , {
context : { 'bin_size' : true , 'future_display_name' : true }
2012-06-27 10:02:06 +00:00
} ) . pipe ( self . on _record _loaded ) ;
2012-01-09 10:45:40 +00:00
} ) ;
2012-06-27 10:02:06 +00:00
}
return shown . pipe ( function ( ) {
2012-08-16 09:38:18 +00:00
self . _actualize _mode ( options . mode || self . options . initial _mode ) ;
2012-08-24 18:27:07 +00:00
self . $el . css ( {
2012-08-01 16:12:04 +00:00
opacity : '1' ,
filter : 'alpha(opacity = 100)'
} ) ;
2011-12-19 16:30:51 +00:00
} ) ;
2011-04-06 21:10:37 +00:00
} ,
do _hide : function ( ) {
2011-07-26 21:00:05 +00:00
if ( this . sidebar ) {
2012-08-24 18:27:07 +00:00
this . sidebar . $el . hide ( ) ;
2011-07-26 21:00:05 +00:00
}
2012-04-09 20:53:34 +00:00
if ( this . $buttons ) {
2012-04-10 07:59:14 +00:00
this . $buttons . hide ( ) ;
2012-04-09 20:53:34 +00:00
}
if ( this . $pager ) {
2012-04-10 11:26:56 +00:00
this . $pager . hide ( ) ;
2012-04-09 20:53:34 +00:00
}
2011-12-14 17:18:11 +00:00
this . _super ( ) ;
2011-04-06 21:10:37 +00:00
} ,
2011-03-30 14:00:48 +00:00
on _record _loaded : function ( record ) {
2012-07-31 14:03:09 +00:00
var self = this , set _values = [ ] ;
2011-06-23 15:55:09 +00:00
if ( ! record ) {
2012-07-05 08:04:50 +00:00
this . set ( { 'title' : undefined } ) ;
2012-02-13 12:38:05 +00:00
this . do _warn ( "Form" , "The record could not be found in the database." , true ) ;
return $ . Deferred ( ) . reject ( ) ;
2011-06-23 15:55:09 +00:00
}
this . datarecord = record ;
2012-07-26 09:56:53 +00:00
this . _actualize _mode ( ) ;
2012-07-31 14:03:09 +00:00
this . set ( { 'title' : record . id ? record . display _name : "New record" } ) ;
2011-11-17 16:00:09 +00:00
2012-04-16 14:45:42 +00:00
if ( this . qweb ) {
this . kill _current _form ( ) ;
this . rendering _engine . set _fields _view ( this . get _fvg _from _qweb ( record ) ) ;
2012-08-24 18:27:07 +00:00
var $dest = this . $el . hasClass ( "oe_form_container" ) ? this . $el : this . $el . find ( '.oe_form_container' ) ;
2012-04-16 14:45:42 +00:00
this . rendering _engine . render _to ( $dest ) ;
}
2011-11-17 16:00:09 +00:00
_ ( this . fields ) . each ( function ( field , f ) {
2012-04-18 11:50:04 +00:00
field . _dirty _flag = false ;
2011-11-17 16:00:09 +00:00
var result = field . set _value ( self . datarecord [ f ] || false ) ;
2012-07-31 14:03:09 +00:00
set _values . push ( result ) ;
2011-11-17 16:00:09 +00:00
} ) ;
2012-07-31 14:03:09 +00:00
return $ . when . apply ( null , set _values ) . pipe ( function ( ) {
2011-11-17 12:43:19 +00:00
if ( ! record . id ) {
2011-12-01 15:50:29 +00:00
// New record: Second pass in order to trigger the onchanges
// respecting the fields order defined in the view
_ . each ( self . fields _order , function ( field _name ) {
if ( record [ field _name ] !== undefined ) {
var field = self . fields [ field _name ] ;
2012-04-18 11:50:04 +00:00
field . _dirty _flag = true ;
2011-11-17 12:43:19 +00:00
self . do _onchange ( field ) ;
}
2011-12-01 15:50:29 +00:00
} ) ;
2011-04-07 13:07:25 +00:00
}
2011-11-17 12:43:19 +00:00
self . on _form _changed ( ) ;
2012-01-10 14:41:13 +00:00
self . is _initialized . resolve ( ) ;
2011-11-17 12:43:19 +00:00
self . do _update _pager ( record . id == null ) ;
if ( self . sidebar ) {
2012-04-17 01:19:33 +00:00
self . sidebar . do _attachement _update ( self . dataset , self . datarecord . id ) ;
2011-11-17 12:43:19 +00:00
}
2011-12-14 16:09:58 +00:00
if ( record . id ) {
self . do _push _state ( { id : record . id } ) ;
2012-08-08 14:16:41 +00:00
} else {
self . do _push _state ( { } ) ;
2011-12-14 16:09:58 +00:00
}
2012-08-24 18:27:07 +00:00
self . $el . add ( self . $buttons ) . removeClass ( 'oe_form_dirty' ) ;
2012-08-01 16:12:04 +00:00
self . autofocus ( ) ;
2011-11-17 12:43:19 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-07-16 11:04:30 +00:00
/ * *
* Loads and sets up the default values for the model as the current
* record
*
* @ return { $ . Deferred }
* /
load _defaults : function ( ) {
var keys = _ . keys ( this . fields _view . fields ) ;
if ( keys . length ) {
return this . dataset . default _get ( keys )
. pipe ( this . on _record _loaded ) ;
}
return this . on _record _loaded ( { } ) ;
} ,
2012-02-15 10:34:05 +00:00
on _form _changed : function ( ) {
2012-03-26 14:41:29 +00:00
this . trigger ( "view_content_has_changed" ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-02-15 10:34:05 +00:00
do _notify _change : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . add ( this . $buttons ) . addClass ( 'oe_form_dirty' ) ;
2012-02-15 10:34:05 +00:00
} ,
2011-04-06 21:10:37 +00:00
on _pager _action : function ( action ) {
2011-09-14 21:09:02 +00:00
if ( this . can _be _discarded ( ) ) {
switch ( action ) {
case 'first' :
this . dataset . index = 0 ;
break ;
case 'previous' :
this . dataset . previous ( ) ;
break ;
case 'next' :
this . dataset . next ( ) ;
break ;
case 'last' :
this . dataset . index = this . dataset . ids . length - 1 ;
break ;
}
this . reload ( ) ;
2011-03-30 14:00:48 +00:00
}
2011-04-06 21:10:37 +00:00
} ,
2012-07-26 11:57:42 +00:00
init _pager : function ( ) {
var self = this ;
if ( this . $pager )
this . $pager . remove ( ) ;
if ( this . get ( "actual_mode" ) === "create" )
return ;
this . $pager = $ ( QWeb . render ( "FormView.pager" , { 'widget' : self } ) ) ;
if ( this . options . $pager ) {
this . $pager . appendTo ( this . options . $pager ) ;
} else {
2012-08-24 18:27:07 +00:00
this . $el . find ( '.oe_form_pager' ) . replaceWith ( this . $pager ) ;
2012-07-26 11:57:42 +00:00
}
this . $pager . on ( 'click' , 'a[data-pager-action]' , function ( ) {
var action = $ ( this ) . data ( 'pager-action' ) ;
self . on _pager _action ( action ) ;
} ) ;
this . do _update _pager ( ) ;
} ,
2011-04-11 11:55:07 +00:00
do _update _pager : function ( hide _index ) {
var index = hide _index ? '-' : this . dataset . index + 1 ;
2012-04-10 11:26:56 +00:00
this . $pager . find ( 'button' ) . prop ( 'disabled' , this . dataset . ids . length < 2 ) . end ( )
. find ( 'span.oe_pager_index' ) . html ( index ) . end ( )
. find ( 'span.oe_pager_count' ) . html ( this . dataset . ids . length ) ;
2011-04-06 21:10:37 +00:00
} ,
2011-11-21 12:04:13 +00:00
parse _on _change : function ( on _change , widget ) {
var self = this ;
var onchange = _ . str . trim ( on _change ) ;
var call = onchange . match ( /^\s?(.*?)\((.*?)\)\s?$/ ) ;
if ( ! call ) {
return null ;
}
2011-11-21 12:45:32 +00:00
var method = call [ 1 ] ;
2011-11-21 12:28:45 +00:00
if ( ! _ . str . trim ( call [ 2 ] ) ) {
2011-11-21 12:45:32 +00:00
return { method : method , args : [ ] , context _index : null }
2011-11-21 12:28:45 +00:00
}
2011-11-21 12:04:13 +00:00
var argument _replacement = {
'False' : function ( ) { return false ; } ,
'True' : function ( ) { return true ; } ,
'None' : function ( ) { return null ; } ,
'context' : function ( i ) {
context _index = i ;
2012-04-17 12:09:49 +00:00
var ctx = new instance . web . CompoundContext ( self . dataset . get _context ( ) , widget . build _context ( ) ? widget . build _context ( ) : { } ) ;
2011-11-21 12:04:13 +00:00
return ctx ;
}
} ;
2011-11-21 12:45:32 +00:00
var parent _fields = null , context _index = null ;
var args = _ . map ( call [ 2 ] . split ( ',' ) , function ( a , i ) {
2011-11-21 12:04:13 +00:00
var field = _ . str . trim ( a ) ;
2011-11-21 12:41:45 +00:00
// literal constant or context
2011-11-21 12:04:13 +00:00
if ( field in argument _replacement ) {
2011-11-21 12:45:32 +00:00
return argument _replacement [ field ] ( i ) ;
2011-11-21 12:41:45 +00:00
}
2011-11-22 12:43:53 +00:00
// literal number
if ( /^-?\d+(\.\d+)?$/ . test ( field ) ) {
return Number ( field ) ;
}
2011-11-21 12:41:45 +00:00
// form field
if ( self . fields [ field ] ) {
2012-04-17 14:32:27 +00:00
var value _ = self . fields [ field ] . get _value ( ) ;
return value _ == null ? false : value _ ;
2011-11-21 12:41:45 +00:00
}
// parent field
var splitted = field . split ( '.' ) ;
if ( splitted . length > 1 && _ . str . trim ( splitted [ 0 ] ) === "parent" && self . dataset . parent _view ) {
if ( parent _fields === null ) {
2011-12-19 17:30:55 +00:00
parent _fields = self . dataset . parent _view . get _fields _values ( [ self . dataset . child _name ] ) ;
2011-11-21 12:04:13 +00:00
}
2011-11-21 12:41:45 +00:00
var p _val = parent _fields [ _ . str . trim ( splitted [ 1 ] ) ] ;
if ( p _val !== undefined ) {
2011-11-21 12:45:32 +00:00
return p _val == null ? false : p _val ;
2011-11-21 12:41:45 +00:00
}
}
// string literal
var first _char = field [ 0 ] , last _char = field [ field . length - 1 ] ;
if ( ( first _char === '"' && last _char === '"' )
|| ( first _char === "'" && last _char === "'" ) ) {
2011-11-21 12:45:32 +00:00
return field . slice ( 1 , - 1 ) ;
2011-11-21 12:04:13 +00:00
}
2011-11-21 12:41:45 +00:00
2011-11-21 12:05:09 +00:00
throw new Error ( "Could not get field with name '" + field +
"' for onchange '" + onchange + "'" ) ;
2011-11-21 12:04:13 +00:00
} ) ;
return {
method : method ,
args : args ,
context _index : context _index
} ;
} ,
2011-04-07 17:09:29 +00:00
do _onchange : function ( widget , processed ) {
2011-10-12 15:08:30 +00:00
var self = this ;
2012-01-10 14:47:09 +00:00
return this . on _change _mutex . exec ( function ( ) {
2011-10-12 15:08:30 +00:00
try {
2012-02-07 16:01:58 +00:00
var response = { } , can _process _onchange = $ . Deferred ( ) ;
2011-11-21 12:04:13 +00:00
processed = processed || [ ] ;
2012-02-07 16:17:33 +00:00
processed . push ( widget . name ) ;
2011-11-21 12:04:13 +00:00
var on _change = widget . node . attrs . on _change ;
if ( on _change ) {
var change _spec = self . parse _on _change ( on _change , widget ) ;
if ( change _spec ) {
var ajax = {
2012-01-20 10:56:08 +00:00
url : '/web/dataset/onchange' ,
2011-11-21 12:04:13 +00:00
async : false
} ;
2012-02-07 16:01:58 +00:00
can _process _onchange = self . rpc ( ajax , {
2011-11-21 12:04:13 +00:00
model : self . dataset . model ,
method : change _spec . method ,
args : [ ( self . datarecord . id == null ? [ ] : [ self . datarecord . id ] ) ] . concat ( change _spec . args ) ,
context _id : change _spec . context _index == undefined ? null : change _spec . context _index + 1
2012-02-07 16:01:58 +00:00
} ) . then ( function ( r ) {
_ . extend ( response , r ) ;
2011-11-21 12:04:13 +00:00
} ) ;
} else {
console . warn ( "Wrong on_change format" , on _change ) ;
}
2011-10-12 15:08:30 +00:00
}
2012-02-07 16:01:58 +00:00
// fail if onchange failed
if ( can _process _onchange . isRejected ( ) ) {
return can _process _onchange ;
}
if ( widget . field [ 'change_default' ] ) {
2012-04-17 14:32:27 +00:00
var fieldname = widget . name , value _ ;
2012-02-07 16:01:58 +00:00
if ( response . value && ( fieldname in response . value ) ) {
// Use value from onchange if onchange executed
2012-04-17 14:32:27 +00:00
value _ = response . value [ fieldname ] ;
2012-02-07 16:01:58 +00:00
} else {
// otherwise get form value for field
2012-04-17 14:32:27 +00:00
value _ = self . fields [ fieldname ] . get _value ( ) ;
2012-02-07 16:01:58 +00:00
}
2012-04-17 14:32:27 +00:00
var condition = fieldname + '=' + value _ ;
2012-02-07 16:01:58 +00:00
2012-04-17 14:32:27 +00:00
if ( value _ ) {
2012-02-07 16:01:58 +00:00
can _process _onchange = self . rpc ( {
url : '/web/dataset/call' ,
async : false
} , {
model : 'ir.values' ,
method : 'get_defaults' ,
args : [ self . model , condition ]
} ) . then ( function ( results ) {
if ( ! results . length ) { return ; }
if ( ! response . value ) {
response . value = { } ;
}
for ( var i = 0 ; i < results . length ; ++ i ) {
// [whatever, key, value]
var triplet = results [ i ] ;
response . value [ triplet [ 1 ] ] = triplet [ 2 ] ;
}
} ) ;
}
}
if ( can _process _onchange . isRejected ( ) ) {
return can _process _onchange ;
}
return self . on _processed _onchange ( response , processed ) ;
2011-10-12 15:08:30 +00:00
} catch ( e ) {
2012-06-22 13:33:12 +00:00
console . error ( e ) ;
2012-06-18 14:02:50 +00:00
instance . webclient . crashmanager . on _javascript _exception ( e ) ;
2011-10-12 15:08:30 +00:00
return $ . Deferred ( ) . reject ( ) ;
}
2012-01-10 14:47:09 +00:00
} ) ;
2011-04-06 14:51:13 +00:00
} ,
2012-06-25 13:52:15 +00:00
on _processed _onchange : function ( result , processed ) {
2011-10-12 15:08:30 +00:00
try {
2011-04-06 14:51:13 +00:00
if ( result . value ) {
for ( var f in result . value ) {
2012-01-20 10:56:08 +00:00
if ( ! result . value . hasOwnProperty ( f ) ) { continue ; }
2011-04-06 14:51:13 +00:00
var field = this . fields [ f ] ;
2011-07-14 13:18:49 +00:00
// If field is not defined in the view, just ignore it
2011-04-06 14:51:13 +00:00
if ( field ) {
2012-04-17 14:32:27 +00:00
var value _ = result . value [ f ] ;
if ( field . get _value ( ) != value _ ) {
field . set _value ( value _ ) ;
2012-04-18 11:50:04 +00:00
field . _dirty _flag = true ;
2012-02-07 16:17:33 +00:00
if ( ! _ . contains ( processed , field . name ) ) {
2011-04-07 17:09:29 +00:00
this . do _onchange ( field , processed ) ;
}
2011-04-06 14:51:13 +00:00
}
}
}
2011-04-07 17:09:29 +00:00
this . on _form _changed ( ) ;
2011-04-06 14:51:13 +00:00
}
2011-07-06 08:32:14 +00:00
if ( ! _ . isEmpty ( result . warning ) ) {
2012-07-13 11:47:37 +00:00
instance . web . dialog ( $ ( QWeb . render ( "CrashManager.warning" , result . warning ) ) , {
2012-05-22 06:28:14 +00:00
title : result . warning . title ,
2011-04-06 14:51:13 +00:00
modal : true ,
2011-12-15 10:40:31 +00:00
buttons : [
{ text : _t ( "Ok" ) , click : function ( ) { $ ( this ) . dialog ( "close" ) ; } }
]
2011-04-06 14:51:13 +00:00
} ) ;
}
if ( result . domain ) {
2012-01-20 10:56:08 +00:00
function edit _domain ( node ) {
2012-06-22 14:33:20 +00:00
if ( typeof node !== "object" ) {
return ;
}
2012-01-20 10:56:08 +00:00
var new _domain = result . domain [ node . attrs . name ] ;
if ( new _domain ) {
node . attrs . domain = new _domain ;
}
_ ( node . children ) . each ( edit _domain ) ;
}
edit _domain ( this . fields _view . arch ) ;
2011-04-06 14:51:13 +00:00
}
2011-10-12 15:08:30 +00:00
return $ . Deferred ( ) . resolve ( ) ;
} catch ( e ) {
console . error ( e ) ;
2012-06-22 13:33:12 +00:00
instance . webclient . crashmanager . on _javascript _exception ( e ) ;
2011-10-12 15:08:30 +00:00
return $ . Deferred ( ) . reject ( ) ;
}
2011-03-30 14:00:48 +00:00
} ,
2012-07-26 10:27:19 +00:00
/ * *
* Ask the view to switch to view mode if possible . The view may not do it
* if the current record is not yet saved . It will then stay in create mode .
* /
2012-07-26 09:51:04 +00:00
to _view _mode : function ( ) {
2012-07-26 09:56:53 +00:00
this . _actualize _mode ( "view" ) ;
2012-07-26 09:51:04 +00:00
} ,
2012-07-26 10:27:19 +00:00
/ * *
* Ask the view to switch to edit mode if possible . The view may not do it
* if the current record is not yet saved . It will then stay in create mode .
* /
2012-07-26 09:51:04 +00:00
to _edit _mode : function ( ) {
2012-07-26 09:56:53 +00:00
this . _actualize _mode ( "edit" ) ;
2012-07-26 09:51:04 +00:00
} ,
/ * *
2012-08-16 09:38:18 +00:00
* Ask the view to switch to a precise mode if possible . The view is free to
* not respect this command if the state of the dataset is not compatible with
* the new mode . For example , it is not possible to switch to edit mode if
* the current record is not yet saved in database .
*
* @ param { string } [ new _mode ] Can be "edit" , "view" , "create" or undefined . If
* undefined the view will test the actual mode to check if it is still consistent
* with the dataset state .
2012-07-26 09:51:04 +00:00
* /
2012-07-26 09:56:53 +00:00
_actualize _mode : function ( switch _to ) {
2012-07-26 09:51:04 +00:00
var mode = switch _to || this . get ( "actual_mode" ) ;
2012-08-02 13:54:03 +00:00
if ( ! this . datarecord . id ) {
2012-07-26 09:51:04 +00:00
mode = "create" ;
2012-08-16 09:38:18 +00:00
} else if ( mode === "create" ) {
mode = "edit" ;
2012-08-02 13:54:03 +00:00
}
2012-07-26 09:51:04 +00:00
this . set ( { actual _mode : mode } ) ;
} ,
2012-07-25 14:31:50 +00:00
check _actual _mode : function ( source , options ) {
2012-04-09 21:30:43 +00:00
var self = this ;
2012-07-25 14:13:33 +00:00
if ( this . get ( "actual_mode" ) === "view" ) {
2012-08-24 18:27:07 +00:00
self . $el . removeClass ( 'oe_form_editable' ) . addClass ( 'oe_form_readonly' ) ;
2012-04-09 21:30:43 +00:00
self . $buttons . find ( '.oe_form_buttons_edit' ) . hide ( ) ;
self . $buttons . find ( '.oe_form_buttons_view' ) . show ( ) ;
2012-04-27 23:41:19 +00:00
self . $sidebar . show ( ) ;
2012-04-09 21:30:43 +00:00
_ . each ( this . fields , function ( field ) {
field . set ( { "force_readonly" : true } ) ;
} ) ;
2012-04-09 23:36:15 +00:00
} else {
2012-08-24 18:27:07 +00:00
self . $el . removeClass ( 'oe_form_readonly' ) . addClass ( 'oe_form_editable' ) ;
2012-04-09 23:36:15 +00:00
self . $buttons . find ( '.oe_form_buttons_edit' ) . show ( ) ;
self . $buttons . find ( '.oe_form_buttons_view' ) . hide ( ) ;
2012-04-27 23:41:19 +00:00
self . $sidebar . hide ( ) ;
2012-04-09 23:36:15 +00:00
_ . each ( this . fields , function ( field ) {
field . set ( { "force_readonly" : false } ) ;
} ) ;
2012-08-01 16:12:04 +00:00
this . autofocus ( ) ;
}
} ,
autofocus : function ( ) {
2012-08-02 09:53:26 +00:00
if ( this . get ( "actual_mode" ) !== "view" && ! this . options . disable _autofocus ) {
2012-08-01 16:12:04 +00:00
var fields _order = this . fields _order . slice ( 0 ) ;
if ( this . default _focus _field ) {
fields _order . unshift ( this . default _focus _field . name ) ;
2012-06-28 13:02:49 +00:00
}
for ( var i = 0 ; i < fields _order . length ; i += 1 ) {
2012-08-01 16:12:04 +00:00
var field = this . fields [ fields _order [ i ] ] ;
2012-07-26 15:47:16 +00:00
if ( ! field . get ( 'effective_invisible' ) && ! field . get ( 'effective_readonly' ) ) {
2012-08-01 16:20:24 +00:00
if ( field . focus ( ) !== false ) {
break ;
}
2012-06-28 13:02:49 +00:00
}
}
2012-04-09 21:30:43 +00:00
}
2012-04-09 20:53:34 +00:00
} ,
2011-11-09 15:12:39 +00:00
on _button _save : function ( ) {
2011-04-07 13:07:25 +00:00
var self = this ;
2012-08-29 10:23:00 +00:00
return this . do _save ( ) . then ( function ( result ) {
2012-07-26 09:51:04 +00:00
self . to _view _mode ( ) ;
2011-04-07 13:07:25 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-04-19 20:44:18 +00:00
on _button _cancel : function ( event ) {
2012-02-15 12:39:01 +00:00
if ( this . can _be _discarded ( ) ) {
2012-08-02 16:54:40 +00:00
if ( this . get ( 'actual_mode' ) === 'create' ) {
this . trigger ( 'history_back' ) ;
} else {
this . to _view _mode ( ) ;
this . on _record _loaded ( this . datarecord ) ;
}
2012-02-15 12:39:01 +00:00
}
2012-04-19 20:44:18 +00:00
return false ;
2011-11-07 13:45:39 +00:00
} ,
2011-12-01 12:22:33 +00:00
on _button _new : function ( ) {
2011-11-07 13:45:39 +00:00
var self = this ;
2012-07-26 09:51:04 +00:00
this . to _edit _mode ( ) ;
2012-06-27 10:01:51 +00:00
return $ . when ( this . has _been _loaded ) . pipe ( function ( ) {
2011-09-15 08:12:41 +00:00
if ( self . can _be _discarded ( ) ) {
2012-07-16 11:04:30 +00:00
return self . load _defaults ( ) ;
2011-09-15 08:12:41 +00:00
}
2011-04-07 13:07:25 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-04-09 20:53:34 +00:00
on _button _edit : function ( ) {
2012-07-26 09:51:04 +00:00
return this . to _edit _mode ( ) ;
2012-04-09 20:53:34 +00:00
} ,
on _button _create : function ( ) {
this . dataset . index = null ;
2012-04-09 23:36:15 +00:00
this . do _show ( ) ;
2012-04-09 20:53:34 +00:00
} ,
on _button _duplicate : function ( ) {
var self = this ;
var def = $ . Deferred ( ) ;
$ . when ( this . has _been _loaded ) . then ( function ( ) {
self . dataset . call ( 'copy' , [ self . datarecord . id , { } , self . dataset . context ] ) . then ( function ( new _id ) {
return self . on _created ( { result : new _id } ) ;
} ) . then ( function ( ) {
2012-07-26 09:51:04 +00:00
return self . to _edit _mode ( ) ;
2012-04-09 20:53:34 +00:00
} ) . then ( function ( ) {
def . resolve ( ) ;
} ) ;
} ) ;
return def . promise ( ) ;
} ,
on _button _delete : function ( ) {
var self = this ;
var def = $ . Deferred ( ) ;
$ . when ( this . has _been _loaded ) . then ( function ( ) {
if ( self . datarecord . id && confirm ( _t ( "Do you really want to delete this record?" ) ) ) {
self . dataset . unlink ( [ self . datarecord . id ] ) . then ( function ( ) {
self . on _pager _action ( 'next' ) ;
def . resolve ( ) ;
} ) ;
} else {
$ . async _when ( ) . then ( function ( ) {
def . reject ( ) ;
} )
}
} ) ;
return def . promise ( ) ;
} ,
2011-09-14 21:09:02 +00:00
can _be _discarded : function ( ) {
2012-09-06 10:50:35 +00:00
return ! this . $el . is ( '.oe_form_dirty' ) || confirm ( _t ( "Warning, the record has been modified, your changes will be discarded.\n\nAre you sure you want to leave this page ?" ) ) ;
2011-09-14 21:09:02 +00:00
} ,
2011-06-06 13:11:08 +00:00
/ * *
* Triggers saving the form ' s record . Chooses between creating a new
* record or saving an existing one depending on whether the record
* already has an id property .
*
2012-08-29 10:23:00 +00:00
* @ param { Boolean } [ prepend _on _create = false ] if ` ` do _save ` ` creates a new
* record , should that record be inserted at the start of the dataset ( by
* default , records are added at the end )
2011-06-06 13:11:08 +00:00
* /
2012-08-29 10:23:00 +00:00
do _save : function ( prepend _on _create ) {
2011-04-11 11:35:16 +00:00
var self = this ;
2012-01-10 14:47:09 +00:00
return this . mutating _mutex . exec ( function ( ) { return self . is _initialized . pipe ( function ( ) {
2011-10-12 14:10:07 +00:00
try {
2011-11-15 14:58:06 +00:00
var form _invalid = false ,
2011-10-11 15:35:35 +00:00
values = { } ,
first _invalid _field = null ;
for ( var f in self . fields ) {
2012-06-27 10:01:51 +00:00
if ( ! self . fields . hasOwnProperty ( f ) ) { continue ; }
2011-10-11 15:35:35 +00:00
f = self . fields [ f ] ;
if ( ! f . is _valid ( ) ) {
form _invalid = true ;
if ( ! first _invalid _field ) {
first _invalid _field = f ;
}
2012-07-20 14:13:04 +00:00
} else if ( f . name !== 'id' && ( ! self . datarecord . id || ( ! f . get ( "readonly" ) && f . _dirty _flag ) ) ) {
2011-11-21 12:40:30 +00:00
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
2011-10-11 15:35:35 +00:00
values [ f . name ] = f . get _value ( ) ;
2011-06-22 12:25:42 +00:00
}
2011-03-30 14:00:48 +00:00
}
2011-10-11 15:35:35 +00:00
if ( form _invalid ) {
2012-04-18 12:20:39 +00:00
self . set ( { 'display_invalid_fields' : true } ) ;
2012-06-27 10:01:51 +00:00
for ( var g in self . fields ) {
if ( ! self . fields . hasOwnProperty ( g ) ) { continue ; }
self . fields [ g ] . _check _css _flags ( ) ;
2012-06-07 14:31:04 +00:00
}
2011-10-11 15:35:35 +00:00
first _invalid _field . focus ( ) ;
self . on _invalid ( ) ;
return $ . Deferred ( ) . reject ( ) ;
2011-04-11 11:35:16 +00:00
} else {
2012-04-18 12:20:39 +00:00
self . set ( { 'display_invalid_fields' : false } ) ;
2011-12-08 12:38:46 +00:00
var save _deferral ;
2011-10-11 15:35:35 +00:00
if ( ! self . datarecord . id ) {
2012-08-29 10:23:00 +00:00
// Creation save
2011-12-08 12:38:46 +00:00
save _deferral = self . dataset . create ( values ) . pipe ( function ( r ) {
2012-08-29 10:23:00 +00:00
return self . on _created ( r , prepend _on _create ) ;
2011-12-08 12:38:46 +00:00
} , null ) ;
2012-04-18 15:18:44 +00:00
} else if ( _ . isEmpty ( values ) && ! self . force _dirty ) {
2012-08-29 10:23:00 +00:00
// Not dirty, noop save
2011-12-08 12:38:46 +00:00
save _deferral = $ . Deferred ( ) . resolve ( { } ) . promise ( ) ;
2011-10-11 15:35:35 +00:00
} else {
2012-04-18 15:18:44 +00:00
self . force _dirty = false ;
2012-08-29 10:23:00 +00:00
// Write save
2011-12-08 12:38:46 +00:00
save _deferral = self . dataset . write ( self . datarecord . id , values , { } ) . pipe ( function ( r ) {
2011-10-11 15:35:35 +00:00
return self . on _saved ( r ) ;
2011-12-08 12:38:46 +00:00
} , null ) ;
2011-10-11 15:35:35 +00:00
}
2012-08-29 10:23:00 +00:00
return save _deferral ;
2011-04-11 11:35:16 +00:00
}
2011-10-12 14:10:07 +00:00
} catch ( e ) {
console . error ( e ) ;
return $ . Deferred ( ) . reject ( ) ;
}
2012-01-10 14:47:09 +00:00
} ) ; } ) ;
2011-04-05 14:34:25 +00:00
} ,
2011-03-30 14:00:48 +00:00
on _invalid : function ( ) {
2012-07-04 15:23:29 +00:00
var warnings = _ ( this . fields ) . chain ( )
. filter ( function ( f ) { return ! f . is _valid ( ) ; } )
. map ( function ( f ) {
return _ . str . sprintf ( '<li>%s</li>' ,
2012-07-11 11:29:59 +00:00
_ . escape ( f . string ) ) ;
2012-07-04 15:23:29 +00:00
} ) . value ( ) ;
warnings . unshift ( '<ul>' ) ;
warnings . push ( '</ul>' ) ;
this . do _warn ( "The following fields are invalid :" , warnings . join ( '' ) ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-08-29 10:23:00 +00:00
/ * *
* Reload the form after saving
*
* @ param { Object } r result of the write function .
* /
on _saved : function ( r ) {
2011-04-05 15:14:40 +00:00
if ( ! r . result ) {
2011-09-12 14:07:53 +00:00
// should not happen in the server, but may happen for internal purpose
2011-09-26 12:39:29 +00:00
return $ . Deferred ( ) . reject ( ) ;
2011-04-05 15:53:48 +00:00
} else {
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
return $ . when ( this . reload ( ) ) . pipe ( function ( ) {
2012-08-29 10:23:00 +00:00
return r ;
} ) ;
2011-04-11 15:59:46 +00:00
}
} ,
2011-06-06 13:11:08 +00:00
/ * *
* Updates the form ' dataset to contain the new record :
*
* * Adds the newly created record to the current dataset ( at the end by
* default )
* * Selects that record ( sets the dataset ' s index to point to the new
* record ' s id ) .
* * Updates the pager and sidebar displays
*
* @ param { Object } r
2012-08-29 10:23:00 +00:00
* @ param { Boolean } [ prepend _on _create = false ] adds the newly created record
* at the beginning of the dataset instead of the end
2011-06-06 13:11:08 +00:00
* /
2012-08-29 10:23:00 +00:00
on _created : function ( r , prepend _on _create ) {
2011-04-11 15:59:46 +00:00
if ( ! r . result ) {
2011-09-12 14:07:53 +00:00
// should not happen in the server, but may happen for internal purpose
2011-09-26 12:39:29 +00:00
return $ . Deferred ( ) . reject ( ) ;
2011-04-11 15:59:46 +00:00
} else {
2011-06-06 13:11:08 +00:00
this . datarecord . id = r . result ;
if ( ! prepend _on _create ) {
2012-02-14 15:20:31 +00:00
this . dataset . alter _ids ( this . dataset . ids . concat ( [ this . datarecord . id ] ) ) ;
2011-06-06 13:11:08 +00:00
this . dataset . index = this . dataset . ids . length - 1 ;
} else {
2012-04-17 01:19:33 +00:00
this . dataset . alter _ids ( [ this . datarecord . id ] . concat ( this . dataset . ids ) ) ;
2011-06-06 13:11:08 +00:00
this . dataset . index = 0 ;
}
2011-04-11 15:59:46 +00:00
this . do _update _pager ( ) ;
2011-07-26 21:00:05 +00:00
if ( this . sidebar ) {
2012-04-17 01:19:33 +00:00
this . sidebar . do _attachement _update ( this . dataset , this . datarecord . id ) ;
2011-07-26 21:00:05 +00:00
}
2012-01-12 14:45:47 +00:00
//openerp.log("The record has been created with id #" + this.datarecord.id);
2012-06-13 15:34:12 +00:00
return $ . when ( this . reload ( ) ) . pipe ( function ( ) {
2012-08-29 10:23:00 +00:00
return _ . extend ( r , { created : true } ) ;
} ) ;
2011-04-05 15:14:40 +00:00
}
2011-03-31 14:44:22 +00:00
} ,
2011-04-01 10:44:54 +00:00
on _action : function ( action ) {
2011-09-12 13:56:03 +00:00
console . debug ( 'Executing action' , action ) ;
2011-04-08 10:37:36 +00:00
} ,
2011-07-26 21:00:05 +00:00
reload : function ( ) {
2011-10-12 14:10:07 +00:00
var self = this ;
2012-01-10 14:47:09 +00:00
return this . reload _mutex . exec ( function ( ) {
2012-04-10 08:33:21 +00:00
if ( self . dataset . index == null ) {
self . do _prev _view ( ) ;
2012-04-09 23:36:15 +00:00
return $ . Deferred ( ) . reject ( ) . promise ( ) ;
}
2011-10-12 14:10:07 +00:00
if ( self . dataset . index == null || self . dataset . index < 0 ) {
return $ . when ( self . on _button _new ( ) ) ;
} else {
2012-07-31 14:03:09 +00:00
var fields = _ . keys ( self . fields _view . fields ) ;
fields . push ( 'display_name' ) ;
return self . dataset . read _index ( fields , {
context : { 'bin_size' : true , 'future_display_name' : true }
2012-01-11 12:49:11 +00:00
} ) . pipe ( self . on _record _loaded ) ;
2011-10-12 14:10:07 +00:00
}
2012-01-10 14:47:09 +00:00
} ) ;
2011-07-26 21:00:05 +00:00
} ,
2012-03-19 17:03:36 +00:00
get _widgets : function ( ) {
return _ . filter ( this . getChildren ( ) , function ( obj ) {
2012-04-26 15:28:00 +00:00
return obj instanceof instance . web . form . FormWidget ;
2012-03-19 17:03:36 +00:00
} ) ;
} ,
2011-12-19 17:30:55 +00:00
get _fields _values : function ( blacklist ) {
2012-07-16 11:13:24 +00:00
blacklist = blacklist || [ ] ;
2011-07-26 21:00:05 +00:00
var values = { } ;
2012-01-18 16:15:17 +00:00
var ids = this . get _selected _ids ( ) ;
values [ "id" ] = ids . length > 0 ? ids [ 0 ] : false ;
2012-04-17 14:32:27 +00:00
_ . each ( this . fields , function ( value _ , key ) {
2012-07-23 17:34:50 +00:00
if ( _ . include ( blacklist , key ) ) {
2012-07-16 11:13:24 +00:00
return ;
2012-06-27 10:01:51 +00:00
}
values [ key ] = value _ . get _value ( ) ;
2011-07-26 21:00:05 +00:00
} ) ;
return values ;
2011-08-26 13:03:35 +00:00
} ,
get _selected _ids : function ( ) {
var id = this . dataset . ids [ this . dataset . index ] ;
return id ? [ id ] : [ ] ;
2011-09-26 12:39:29 +00:00
} ,
recursive _save : function ( ) {
var self = this ;
return $ . when ( this . do _save ( ) ) . pipe ( function ( res ) {
2011-09-26 16:00:57 +00:00
if ( self . dataset . parent _view )
return self . dataset . parent _view . recursive _save ( ) ;
2011-09-26 12:39:29 +00:00
} ) ;
2011-09-30 15:46:12 +00:00
} ,
2011-11-15 14:58:06 +00:00
is _dirty : function ( ) {
2012-04-17 14:32:27 +00:00
return _ . any ( this . fields , function ( value _ ) {
2012-04-18 11:50:04 +00:00
return value _ . _dirty _flag ;
2011-11-16 07:52:46 +00:00
} ) ;
2011-11-15 14:58:06 +00:00
} ,
2011-09-30 15:46:12 +00:00
is _interactible _record : function ( ) {
var id = this . datarecord . id ;
if ( ! id ) {
if ( this . options . not _interactible _on _create )
return false ;
} else if ( typeof ( id ) === "string" ) {
2012-04-17 12:09:49 +00:00
if ( instance . web . BufferedDataSet . virtual _id _regex . test ( id ) )
2011-09-30 15:46:12 +00:00
return false ;
}
return true ;
2011-11-21 14:14:18 +00:00
} ,
sidebar _context : function ( ) {
2011-12-19 17:30:55 +00:00
return this . do _save ( ) . pipe ( _ . bind ( function ( ) { return this . get _fields _values ( ) ; } , this ) ) ;
2012-02-08 12:21:53 +00:00
} ,
open _defaults _dialog : function ( ) {
var self = this ;
var fields = _ . chain ( this . fields )
. map ( function ( field , name ) {
2012-05-22 14:16:49 +00:00
var value = field . get _value ( ) ;
2012-02-08 12:21:53 +00:00
// ignore fields which are empty, invisible, readonly, o2m
// or m2m
2012-05-22 14:16:49 +00:00
if ( ! value
2012-03-26 16:29:34 +00:00
|| field . get ( 'invisible' )
2012-03-20 15:51:11 +00:00
|| field . get ( "readonly" )
2012-02-08 12:21:53 +00:00
|| field . field . type === 'one2many'
|| field . field . type === 'many2many' ) {
return false ;
}
2012-05-22 14:16:49 +00:00
var displayed = value ;
2012-08-23 10:16:01 +00:00
if ( field instanceof instance . web . form . FieldSelection ) {
2012-02-09 15:14:10 +00:00
displayed = _ ( field . values ) . find ( function ( option ) {
2012-05-22 14:16:49 +00:00
return option [ 0 ] === value ;
2012-02-09 15:14:10 +00:00
} ) [ 1 ] ;
2012-08-23 10:16:01 +00:00
} else if ( field instanceof instance . web . form . FieldMany2One ) {
2012-07-19 08:19:14 +00:00
displayed = field . get _displayed ( ) ;
2012-02-09 15:14:10 +00:00
}
2012-02-08 12:21:53 +00:00
return {
name : name ,
2012-06-29 15:02:25 +00:00
string : field . string ,
2012-05-22 14:16:49 +00:00
value : value ,
2012-02-09 15:14:10 +00:00
displayed : displayed ,
2012-02-08 12:21:53 +00:00
// convert undefined to false
change _default : ! ! field . field . change _default
}
} )
. compact ( )
2012-05-22 14:13:42 +00:00
. sortBy ( function ( field ) { return field . string ; } )
2012-02-08 12:21:53 +00:00
. value ( ) ;
var conditions = _ . chain ( fields )
. filter ( function ( field ) { return field . change _default ; } )
. value ( ) ;
2012-04-17 12:09:49 +00:00
var d = new instance . web . Dialog ( this , {
2012-02-08 12:21:53 +00:00
title : _t ( "Set Default" ) ,
args : {
fields : fields ,
conditions : conditions
} ,
buttons : [
{ text : _t ( "Close" ) , click : function ( ) { d . close ( ) ; } } ,
{ text : _t ( "Save default" ) , click : function ( ) {
2012-08-24 18:27:07 +00:00
var $defaults = d . $el . find ( '#formview_default_fields' ) ;
2012-02-08 12:21:53 +00:00
var field _to _set = $defaults . val ( ) ;
if ( ! field _to _set ) {
2012-03-26 12:29:50 +00:00
$defaults . parent ( ) . addClass ( 'oe_form_invalid' ) ;
2012-02-08 12:21:53 +00:00
return ;
}
2012-08-24 18:27:07 +00:00
var condition = d . $el . find ( '#formview_default_conditions' ) . val ( ) ,
all _users = d . $el . find ( '#formview_default_all' ) . is ( ':checked' ) ;
2012-04-17 12:09:49 +00:00
new instance . web . DataSet ( self , 'ir.values' ) . call (
2012-02-08 12:21:53 +00:00
'set_default' , [
self . dataset . model ,
field _to _set ,
self . fields [ field _to _set ] . get _value ( ) ,
all _users ,
false ,
condition || false
] ) . then ( function ( ) { d . close ( ) ; } ) ;
} }
]
} ) ;
d . template = 'FormView.set_default' ;
d . open ( ) ;
2012-04-16 15:08:39 +00:00
} ,
register _field : function ( field , name ) {
this . fields [ name ] = field ;
this . fields _order . push ( name ) ;
2012-07-26 15:47:16 +00:00
if ( JSON . parse ( field . node . attrs . default _focus || "0" ) ) {
2012-06-28 13:02:49 +00:00
this . default _focus _field = field ;
}
2012-06-25 13:52:15 +00:00
2012-06-25 14:40:13 +00:00
field . on ( 'focused' , null , this . proxy ( 'widgetFocused' ) )
. on ( 'blurred' , null , this . proxy ( 'widgetBlurred' ) ) ;
2012-04-17 13:33:07 +00:00
if ( this . get _field ( name ) . translate ) {
2012-04-17 14:32:27 +00:00
this . translatable _fields . push ( field ) ;
2012-04-17 13:33:07 +00:00
}
2012-04-18 09:57:58 +00:00
field . on ( 'changed_value' , this , function ( ) {
2012-04-18 13:31:37 +00:00
field . _dirty _flag = true ;
2012-04-18 11:50:04 +00:00
if ( field . is _syntax _valid ( ) ) {
this . do _onchange ( field ) ;
this . on _form _changed ( true ) ;
this . do _notify _change ( ) ;
}
2012-04-18 09:57:58 +00:00
} ) ;
2012-04-17 13:33:07 +00:00
} ,
get _field : function ( field _name ) {
return this . fields _view . fields [ field _name ] ;
2012-04-16 15:08:39 +00:00
} ,
2012-04-17 14:32:27 +00:00
is _create _mode : function ( ) {
2012-07-25 14:13:33 +00:00
return this . get ( "actual_mode" ) === "create" ;
2012-04-17 14:32:27 +00:00
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2012-03-14 17:09:49 +00:00
2012-03-20 16:26:16 +00:00
/ * *
* Interface to be implemented by rendering engines for the form view .
* /
2012-05-02 09:10:53 +00:00
instance . web . form . FormRenderingEngineInterface = instance . web . Class . extend ( {
2012-03-20 16:26:16 +00:00
set _fields _view : function ( fields _view ) { } ,
2012-04-10 12:21:10 +00:00
set _fields _registry : function ( fields _registry ) { } ,
2012-08-24 18:27:07 +00:00
render _to : function ( $el ) { } ,
2012-05-02 09:10:53 +00:00
} ) ;
2012-03-20 16:26:16 +00:00
2012-03-20 16:18:06 +00:00
/ * *
* Default rendering engine for the form view .
2012-07-12 12:56:37 +00:00
*
2012-03-20 16:18:06 +00:00
* It is necessary to set the view using set _view ( ) before usage .
* /
2012-05-02 09:10:53 +00:00
instance . web . form . FormRenderingEngine = instance . web . form . FormRenderingEngineInterface . extend ( {
2012-03-20 16:18:06 +00:00
init : function ( view ) {
this . view = view ;
} ,
set _fields _view : function ( fvg ) {
2012-03-14 17:09:49 +00:00
this . fvg = fvg ;
2012-06-12 08:10:58 +00:00
this . version = parseFloat ( this . fvg . arch . attrs . version ) ;
if ( isNaN ( this . version ) ) {
this . version = 6.1 ;
}
2012-03-20 16:18:06 +00:00
} ,
2012-04-10 12:09:38 +00:00
set _tags _registry : function ( tags _registry ) {
this . tags _registry = tags _registry ;
2012-03-20 16:18:06 +00:00
} ,
2012-04-10 12:21:10 +00:00
set _fields _registry : function ( fields _registry ) {
this . fields _registry = fields _registry ;
} ,
2012-06-09 16:00:09 +00:00
// Backward compatibility tools, current default version: v6.1
process _version : function ( ) {
2012-06-12 08:10:58 +00:00
if ( this . version < 7.0 ) {
this . $form . find ( 'form:first' ) . wrapInner ( '<group col="4"/>' ) ;
this . $form . find ( 'page' ) . each ( function ( ) {
if ( ! $ ( this ) . parents ( 'field' ) . length ) {
$ ( this ) . wrapInner ( '<group col="4"/>' ) ;
}
} ) ;
}
2012-06-09 16:00:09 +00:00
} ,
2012-04-10 12:21:10 +00:00
render _to : function ( $target ) {
2012-03-20 16:18:06 +00:00
var self = this ;
2012-04-10 12:21:10 +00:00
this . $target = $target ;
2012-03-21 10:53:36 +00:00
2012-03-14 17:09:49 +00:00
// TODO: I know this will save the world and all the kitten for a moment,
// but one day, we will have to get rid of xml2json
2012-04-17 12:09:49 +00:00
var xml = instance . web . json _node _to _xml ( this . fvg . arch ) ;
2012-03-28 10:41:18 +00:00
this . $form = $ ( '<div class="oe_form">' + xml + '</div>' ) ;
2012-03-14 17:09:49 +00:00
2012-06-12 08:10:58 +00:00
this . process _version ( ) ;
2012-06-09 16:00:09 +00:00
2012-04-10 13:04:44 +00:00
this . fields _to _init = [ ] ;
this . tags _to _init = [ ] ;
2012-03-27 12:47:04 +00:00
this . labels = { } ;
2012-03-20 14:35:16 +00:00
this . process ( this . $form ) ;
2012-03-21 10:53:36 +00:00
2012-04-10 12:21:10 +00:00
this . $form . appendTo ( this . $target ) ;
2012-03-14 17:09:49 +00:00
2012-04-10 13:04:44 +00:00
_ . each ( this . fields _to _init , function ( $elem ) {
var name = $elem . attr ( "name" ) ;
if ( ! self . fvg . fields [ name ] ) {
throw new Error ( "Field '" + name + "' specified in view could not be found." ) ;
}
2012-04-10 13:26:57 +00:00
var obj = self . fields _registry . get _any ( [ $elem . attr ( 'widget' ) , self . fvg . fields [ name ] . type ] ) ;
if ( ! obj ) {
2012-05-25 19:22:08 +00:00
throw new Error ( "Widget type '" + $elem . attr ( 'widget' ) + "' is not implemented" ) ;
2012-04-10 13:04:44 +00:00
}
2012-04-17 12:09:49 +00:00
var w = new ( obj ) ( self . view , instance . web . xml _to _json ( $elem [ 0 ] ) ) ;
2012-04-10 13:04:44 +00:00
var $label = self . labels [ $elem . attr ( "name" ) ] ;
if ( $label ) {
w . set _input _id ( $label . attr ( "for" ) ) ;
2012-03-14 17:09:49 +00:00
}
2012-04-10 13:04:44 +00:00
self . alter _field ( w ) ;
2012-04-16 15:08:39 +00:00
self . view . register _field ( w , $elem . attr ( "name" ) ) ;
2012-04-10 13:04:44 +00:00
w . replace ( $elem ) ;
2012-03-14 17:09:49 +00:00
} ) ;
2012-04-10 13:04:44 +00:00
_ . each ( this . tags _to _init , function ( $elem ) {
var tag _name = $elem [ 0 ] . tagName . toLowerCase ( ) ;
var obj = self . tags _registry . get _object ( tag _name ) ;
2012-04-17 12:09:49 +00:00
var w = new ( obj ) ( self . view , instance . web . xml _to _json ( $elem [ 0 ] ) ) ;
2012-04-10 13:04:44 +00:00
w . replace ( $elem ) ;
2012-05-30 09:34:46 +00:00
} ) ;
2012-04-16 14:45:42 +00:00
// TODO: return a deferred
2012-03-21 10:53:36 +00:00
} ,
2012-05-31 09:08:30 +00:00
render _element : function ( template /* dictionaries */ ) {
var dicts = [ ] . slice . call ( arguments ) . slice ( 1 ) ;
2012-04-10 10:29:51 +00:00
var dict = _ . extend . apply ( _ , dicts ) ;
dict [ 'classnames' ] = dict [ 'class' ] || '' ; // class is a reserved word and might caused problem to Safari when used from QWeb
2012-03-21 10:53:36 +00:00
return $ ( QWeb . render ( template , dict ) ) ;
2012-03-20 11:01:15 +00:00
} ,
2012-04-09 21:43:48 +00:00
alter _field : function ( field ) {
} ,
2012-03-21 10:53:36 +00:00
toggle _layout _debugging : function ( ) {
2012-04-10 12:21:10 +00:00
if ( ! this . $target . has ( '.oe_layout_debug_cell:first' ) . length ) {
2012-04-18 16:10:39 +00:00
this . $target . find ( '[title]' ) . removeAttr ( 'title' ) ;
2012-04-10 12:21:10 +00:00
this . $target . find ( '.oe_form_group_cell' ) . each ( function ( ) {
2012-04-18 16:10:39 +00:00
var text = 'W:' + ( $ ( this ) . attr ( 'width' ) || '' ) + ' - C:' + $ ( this ) . attr ( 'colspan' ) ;
$ ( this ) . attr ( 'title' , text ) ;
2012-03-20 11:01:15 +00:00
} ) ;
}
2012-04-10 12:21:10 +00:00
this . $target . toggleClass ( 'oe_layout_debugging' ) ;
2012-03-14 17:09:49 +00:00
} ,
2012-05-31 09:08:30 +00:00
process : function ( $tag ) {
2012-03-20 11:05:29 +00:00
var self = this ;
var tagname = $tag [ 0 ] . nodeName . toLowerCase ( ) ;
2012-04-10 13:04:44 +00:00
if ( this . tags _registry . contains ( tagname ) ) {
this . tags _to _init . push ( $tag ) ;
return $tag ;
2012-03-20 11:05:29 +00:00
}
2012-04-10 13:04:44 +00:00
var fn = self [ 'process_' + tagname ] ;
2012-03-20 14:35:16 +00:00
if ( fn ) {
var args = [ ] . slice . call ( arguments ) ;
args [ 0 ] = $tag ;
2012-03-20 15:04:57 +00:00
return fn . apply ( self , args ) ;
2012-03-20 14:35:16 +00:00
} else {
// generic tag handling, just process children
$tag . children ( ) . each ( function ( ) {
2012-05-31 09:08:30 +00:00
self . process ( $ ( this ) ) ;
2012-03-20 11:05:29 +00:00
} ) ;
2012-03-29 09:55:24 +00:00
self . handle _common _properties ( $tag , $tag ) ;
2012-03-27 09:39:38 +00:00
$tag . removeAttr ( "modifiers" ) ;
2012-03-20 15:04:57 +00:00
return $tag ;
2012-03-20 11:05:29 +00:00
}
} ,
2012-05-31 09:08:30 +00:00
process _sheet : function ( $sheet ) {
var $new _sheet = this . render _element ( 'FormRenderingSheet' , $sheet . getAttributes ( ) ) ;
2012-04-24 10:37:12 +00:00
this . handle _common _properties ( $new _sheet , $sheet ) ;
2012-05-31 09:08:30 +00:00
var $dst = $new _sheet . find ( '.oe_form_sheet' ) ;
2012-05-30 09:34:46 +00:00
$sheet . contents ( ) . appendTo ( $dst ) ;
2012-04-24 10:37:12 +00:00
$sheet . before ( $new _sheet ) . remove ( ) ;
2012-05-31 09:08:30 +00:00
this . process ( $new _sheet ) ;
2012-04-10 10:29:51 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _form : function ( $form ) {
2012-06-04 11:44:33 +00:00
if ( $form . find ( '> sheet' ) . length === 0 ) {
2012-06-04 10:11:45 +00:00
$form . addClass ( 'oe_form_nosheet' ) ;
}
2012-05-31 09:08:30 +00:00
var $new _form = this . render _element ( 'FormRenderingForm' , $form . getAttributes ( ) ) ;
2012-04-24 10:37:12 +00:00
this . handle _common _properties ( $new _form , $form ) ;
2012-05-31 09:08:30 +00:00
$form . contents ( ) . appendTo ( $new _form ) ;
2012-03-21 15:57:22 +00:00
if ( $form [ 0 ] === this . $form [ 0 ] ) {
// If root element, replace it
this . $form = $new _form ;
2012-03-21 10:53:36 +00:00
} else {
2012-03-21 15:57:22 +00:00
$form . before ( $new _form ) . remove ( ) ;
2012-03-21 10:53:36 +00:00
}
2012-05-31 09:08:30 +00:00
this . process ( $new _form ) ;
2012-03-21 10:53:36 +00:00
} ,
2012-05-28 17:21:14 +00:00
/ *
* Used by direct < field > children of a < group > tag only
* This method will add the implicit < label ... > for every field
* in the < group >
* /
2012-03-20 15:18:01 +00:00
preprocess _field : function ( $field ) {
2012-04-10 13:17:46 +00:00
var self = this ;
2012-03-14 17:09:49 +00:00
var name = $field . attr ( 'name' ) ,
2012-03-26 16:29:34 +00:00
field _colspan = parseInt ( $field . attr ( 'colspan' ) , 10 ) ,
field _modifiers = JSON . parse ( $field . attr ( 'modifiers' ) || '{}' ) ;
2012-04-10 10:29:51 +00:00
2012-03-27 12:21:54 +00:00
if ( $field . attr ( 'nolabel' ) === '1' )
return ;
$field . attr ( 'nolabel' , '1' ) ;
var found = false ;
this . $form . find ( 'label[for="' + name + '"]' ) . each ( function ( i , el ) {
2012-04-10 13:17:46 +00:00
$ ( el ) . parents ( ) . each ( function ( unused , tag ) {
2012-04-10 13:20:31 +00:00
var name = tag . tagName . toLowerCase ( ) ;
if ( name === "field" || name in self . tags _registry . map )
2012-04-10 13:17:46 +00:00
found = true ;
} ) ;
2012-03-27 12:21:54 +00:00
} ) ;
if ( found )
return ;
2012-04-10 10:29:51 +00:00
2012-06-27 10:01:51 +00:00
var $label = $ ( '<label/>' ) . attr ( {
2012-03-27 12:21:54 +00:00
'for' : name ,
"modifiers" : JSON . stringify ( { invisible : field _modifiers . invisible } ) ,
"string" : $field . attr ( 'string' ) ,
"help" : $field . attr ( 'help' ) ,
2012-06-05 12:53:32 +00:00
"class" : $field . attr ( 'class' ) ,
2012-03-27 12:21:54 +00:00
} ) ;
$label . insertBefore ( $field ) ;
if ( field _colspan > 1 ) {
$field . attr ( 'colspan' , field _colspan - 1 ) ;
2012-03-20 15:18:01 +00:00
}
2012-03-27 12:21:54 +00:00
return $label ;
2012-03-20 15:18:01 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _field : function ( $field ) {
2012-05-29 15:10:55 +00:00
if ( $field . parent ( ) . is ( 'group' ) ) {
// No implicit labels for normal fields, only for <group> direct children
var $label = this . preprocess _field ( $field ) ;
if ( $label ) {
2012-05-31 09:08:30 +00:00
this . process ( $label ) ;
2012-05-29 15:10:55 +00:00
}
}
2012-04-10 13:04:44 +00:00
this . fields _to _init . push ( $field ) ;
2012-03-20 15:18:01 +00:00
return $field ;
2012-03-14 17:09:49 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _group : function ( $group ) {
2012-03-20 11:05:29 +00:00
var self = this ;
2012-03-20 15:18:01 +00:00
$group . children ( 'field' ) . each ( function ( ) {
self . preprocess _field ( $ ( this ) ) ;
2012-03-20 11:05:29 +00:00
} ) ;
2012-05-31 09:08:30 +00:00
var $new _group = this . render _element ( 'FormRenderingGroup' , $group . getAttributes ( ) ) ;
2012-05-29 15:10:55 +00:00
var $table ;
if ( $new _group . first ( ) . is ( 'table.oe_form_group' ) ) {
2012-03-14 17:09:49 +00:00
$table = $new _group ;
2012-05-29 15:10:55 +00:00
} else if ( $new _group . filter ( 'table.oe_form_group' ) . length ) {
$table = $new _group . filter ( 'table.oe_form_group' ) . first ( ) ;
2012-03-14 17:09:49 +00:00
} else {
2012-05-29 15:10:55 +00:00
$table = $new _group . find ( 'table.oe_form_group' ) . first ( ) ;
2012-03-14 17:09:49 +00:00
}
2012-05-28 17:21:14 +00:00
2012-03-26 09:44:18 +00:00
var $tr , $td ,
2012-06-01 15:32:11 +00:00
cols = parseInt ( $group . attr ( 'col' ) || 2 , 10 ) ,
2012-03-14 17:09:49 +00:00
row _cols = cols ;
2012-03-20 13:19:39 +00:00
var children = [ ] ;
2012-03-26 09:44:18 +00:00
$group . children ( ) . each ( function ( a , b , c ) {
2012-04-25 14:23:36 +00:00
var $child = $ ( this ) ;
var colspan = parseInt ( $child . attr ( 'colspan' ) || 1 , 10 ) ;
var tagName = $child [ 0 ] . tagName . toLowerCase ( ) ;
var $td = $ ( '<td/>' ) . addClass ( 'oe_form_group_cell' ) . attr ( 'colspan' , colspan ) ;
var newline = tagName === 'newline' ;
2012-05-28 17:21:14 +00:00
2012-05-29 15:10:55 +00:00
// Note FME: those classes are used in layout debug mode
2012-04-25 14:23:36 +00:00
if ( $tr && row _cols > 0 && ( newline || row _cols < colspan ) ) {
$tr . addClass ( 'oe_form_group_row_incomplete' ) ;
if ( newline ) {
$tr . addClass ( 'oe_form_group_row_newline' ) ;
}
}
if ( newline ) {
2012-03-14 17:09:49 +00:00
$tr = null ;
return ;
}
if ( ! $tr || row _cols < colspan ) {
$tr = $ ( '<tr/>' ) . addClass ( 'oe_form_group_row' ) . appendTo ( $table ) ;
row _cols = cols ;
2012-07-10 07:31:45 +00:00
} else if ( tagName === 'group' ) {
// When <group> <group/><group/> </group>, we need a spacing between the two groups
2012-07-16 11:13:24 +00:00
$td . addClass ( 'oe_group_right' )
2012-03-14 17:09:49 +00:00
}
row _cols -= colspan ;
2012-04-10 10:29:51 +00:00
2012-03-26 16:49:05 +00:00
// invisibility transfer
2012-03-27 09:39:38 +00:00
var field _modifiers = JSON . parse ( $child . attr ( 'modifiers' ) || '{}' ) ;
var invisible = field _modifiers . invisible ;
2012-03-29 09:55:24 +00:00
self . handle _common _properties ( $td , $ ( "<dummy>" ) . attr ( "modifiers" , JSON . stringify ( { invisible : invisible } ) ) ) ;
2012-04-10 10:29:51 +00:00
2012-03-14 17:09:49 +00:00
$tr . append ( $td . append ( $child ) ) ;
2012-03-20 13:19:39 +00:00
children . push ( $child [ 0 ] ) ;
2012-03-14 17:09:49 +00:00
} ) ;
2012-03-27 10:18:32 +00:00
if ( row _cols && $td ) {
2012-03-26 09:44:18 +00:00
$td . attr ( 'colspan' , parseInt ( $td . attr ( 'colspan' ) , 10 ) + row _cols ) ;
}
2012-03-14 17:09:49 +00:00
$group . before ( $new _group ) . remove ( ) ;
2012-03-15 21:58:10 +00:00
2012-04-10 13:04:20 +00:00
$table . find ( '> tbody > tr' ) . each ( function ( ) {
2012-03-15 19:32:45 +00:00
var to _compute = [ ] ,
2012-03-20 12:06:21 +00:00
row _cols = cols ,
2012-03-15 19:32:45 +00:00
total = 100 ;
2012-03-15 21:58:10 +00:00
$ ( this ) . children ( ) . each ( function ( ) {
2012-03-15 19:32:45 +00:00
var $td = $ ( this ) ,
$child = $td . children ( ':first' ) ;
switch ( $child [ 0 ] . tagName . toLowerCase ( ) ) {
case 'separator' :
break ;
case 'label' :
if ( $child . attr ( 'for' ) ) {
2012-04-24 09:03:50 +00:00
$td . attr ( 'width' , '1%' ) . addClass ( 'oe_form_group_cell_label' ) ;
2012-06-04 20:44:49 +00:00
row _cols -= $td . attr ( 'colspan' ) || 1 ;
2012-03-15 19:32:45 +00:00
total -- ;
}
break ;
default :
2012-04-19 15:33:41 +00:00
var width = _ . str . trim ( $child . attr ( 'width' ) || '' ) ,
iwidth = parseInt ( width , 10 ) ;
if ( iwidth ) {
if ( width . substr ( - 1 ) === '%' ) {
total -= iwidth ;
width = iwidth + '%' ;
2012-05-03 14:30:43 +00:00
} else {
// Absolute width
$td . css ( 'min-width' , width + 'px' ) ;
2012-04-19 15:33:41 +00:00
}
$td . attr ( 'width' , width ) ;
$child . removeAttr ( 'width' ) ;
2012-06-04 21:00:31 +00:00
row _cols -= $td . attr ( 'colspan' ) || 1 ;
2012-04-19 15:33:41 +00:00
} else {
to _compute . push ( $td ) ;
}
2012-03-15 19:32:45 +00:00
}
} ) ;
2012-06-04 21:00:31 +00:00
if ( row _cols ) {
var unit = Math . floor ( total / row _cols ) ;
if ( ! $ ( this ) . is ( '.oe_form_group_row_incomplete' ) ) {
_ . each ( to _compute , function ( $td , i ) {
var width = parseInt ( $td . attr ( 'colspan' ) , 10 ) * unit ;
$td . attr ( 'width' , width + '%' ) ;
total -= width ;
} ) ;
}
2012-04-25 14:23:36 +00:00
}
2012-03-15 21:58:10 +00:00
} ) ;
2012-03-20 13:19:39 +00:00
_ . each ( children , function ( el ) {
2012-03-20 15:18:01 +00:00
self . process ( $ ( el ) ) ;
2012-03-20 13:19:39 +00:00
} ) ;
2012-03-29 09:55:24 +00:00
this . handle _common _properties ( $new _group , $group ) ;
2012-03-20 15:04:57 +00:00
return $new _group ;
2012-03-15 19:32:45 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _notebook : function ( $notebook ) {
2012-03-20 11:05:29 +00:00
var self = this ;
2012-03-15 10:53:24 +00:00
var pages = [ ] ;
$notebook . find ( '> page' ) . each ( function ( ) {
2012-04-01 21:55:07 +00:00
var $page = $ ( this ) ;
var page _attrs = $page . getAttributes ( ) ;
2012-03-15 10:53:24 +00:00
page _attrs . id = _ . uniqueId ( 'notebook_page_' ) ;
2012-05-31 09:08:30 +00:00
var $new _page = self . render _element ( 'FormRenderingNotebookPage' , page _attrs ) ;
$page . contents ( ) . appendTo ( $new _page ) ;
2012-03-15 10:53:24 +00:00
$page . before ( $new _page ) . remove ( ) ;
2012-06-07 10:41:23 +00:00
var ic = self . handle _common _properties ( $new _page , $page ) . invisibility _changer ;
page _attrs . _ _page = $new _page ;
page _attrs . _ _ic = ic ;
pages . push ( page _attrs ) ;
2012-07-12 12:56:37 +00:00
2012-06-07 10:41:23 +00:00
$new _page . children ( ) . each ( function ( ) {
self . process ( $ ( this ) ) ;
} ) ;
2012-03-15 10:53:24 +00:00
} ) ;
2012-05-31 09:08:30 +00:00
var $new _notebook = this . render _element ( 'FormRenderingNotebook' , { pages : pages } ) ;
2012-05-30 09:34:46 +00:00
$notebook . contents ( ) . appendTo ( $new _notebook ) ;
2012-03-15 10:53:24 +00:00
$notebook . before ( $new _notebook ) . remove ( ) ;
2012-06-07 10:41:23 +00:00
self . process ( $ ( $new _notebook . children ( ) [ 0 ] ) ) ;
//tabs and invisibility handling
2012-03-15 10:53:24 +00:00
$new _notebook . tabs ( ) ;
2012-06-07 10:41:23 +00:00
_ . each ( pages , function ( page , i ) {
if ( ! page . _ _ic )
return ;
page . _ _ic . on ( "change:effective_invisible" , null , function ( ) {
var current = $new _notebook . tabs ( "option" , "selected" ) ;
if ( ! pages [ current ] . _ _ic || ! pages [ current ] . _ _ic . get ( "effective_invisible" ) )
return ;
var first _visible = _ . find ( _ . range ( pages . length ) , function ( i2 ) {
return ( ! pages [ i2 ] . _ _ic ) || ( ! pages [ i2 ] . _ _ic . get ( "effective_invisible" ) ) ;
} ) ;
if ( first _visible !== undefined ) {
$new _notebook . tabs ( 'select' , first _visible ) ;
}
} ) ;
} ) ;
2012-07-12 12:56:37 +00:00
2012-03-29 09:55:24 +00:00
this . handle _common _properties ( $new _notebook , $notebook ) ;
2012-03-20 15:04:57 +00:00
return $new _notebook ;
2012-03-14 17:09:49 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _separator : function ( $separator ) {
var $new _separator = this . render _element ( 'FormRenderingSeparator' , $separator . getAttributes ( ) ) ;
2012-03-15 14:16:22 +00:00
$separator . before ( $new _separator ) . remove ( ) ;
2012-03-29 09:55:24 +00:00
this . handle _common _properties ( $new _separator , $separator ) ;
2012-03-20 15:04:57 +00:00
return $new _separator ;
2012-03-14 17:09:49 +00:00
} ,
2012-05-31 09:08:30 +00:00
process _label : function ( $label ) {
2012-03-27 12:21:54 +00:00
var name = $label . attr ( "for" ) ,
field _orm = this . fvg . fields [ name ] ;
var dict = {
2012-03-27 14:31:33 +00:00
string : $label . attr ( 'string' ) || ( field _orm || { } ) . string || '' ,
help : $label . attr ( 'help' ) || ( field _orm || { } ) . help || '' ,
2012-03-29 15:01:36 +00:00
_for : name ? _ . uniqueId ( 'oe-field-input-' ) : undefined ,
2012-03-27 12:21:54 +00:00
} ;
2012-03-19 10:29:04 +00:00
var align = parseFloat ( dict . align ) ;
if ( isNaN ( align ) || align === 1 ) {
align = 'right' ;
} else if ( align === 0 ) {
align = 'left' ;
} else {
align = 'center' ;
}
dict . align = align ;
2012-05-31 09:08:30 +00:00
var $new _label = this . render _element ( 'FormRenderingLabel' , dict ) ;
2012-03-14 17:09:49 +00:00
$label . before ( $new _label ) . remove ( ) ;
2012-03-29 09:55:24 +00:00
this . handle _common _properties ( $new _label , $label ) ;
2012-03-29 15:01:36 +00:00
if ( name ) {
this . labels [ name ] = $new _label ;
}
2012-03-20 15:04:57 +00:00
return $new _label ;
2012-03-26 15:14:37 +00:00
} ,
2012-04-10 12:21:10 +00:00
handle _common _properties : function ( $new _element , $node ) {
2012-06-27 10:01:51 +00:00
var str _modifiers = $node . attr ( "modifiers" ) || "{}" ;
2012-03-29 09:55:24 +00:00
var modifiers = JSON . parse ( str _modifiers ) ;
2012-06-07 10:41:23 +00:00
var ic = null ;
2012-03-29 09:55:24 +00:00
if ( modifiers . invisible !== undefined )
2012-06-07 10:41:23 +00:00
ic = new instance . web . form . InvisibilityChanger ( this . view , this . view , modifiers . invisible , $new _element ) ;
2012-04-10 12:21:10 +00:00
$new _element . addClass ( $node . attr ( "class" ) || "" ) ;
2012-04-24 12:22:49 +00:00
$new _element . attr ( 'style' , $node . attr ( 'style' ) ) ;
2012-06-07 10:41:23 +00:00
return { invisibility _changer : ic , } ;
2012-03-26 16:29:34 +00:00
} ,
2012-03-14 17:09:49 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FormDialog = instance . web . Dialog . extend ( {
2011-09-07 14:53:11 +00:00
init : function ( parent , options , view _id , dataset ) {
this . _super ( parent , options ) ;
this . dataset = dataset ;
this . view _id = view _id ;
return this ;
} ,
start : function ( ) {
this . _super ( ) ;
2012-04-17 12:09:49 +00:00
this . form = new instance . web . FormView ( this , this . dataset , this . view _id , {
2011-09-07 14:53:11 +00:00
pager : false
} ) ;
2012-08-24 18:27:07 +00:00
this . form . appendTo ( this . $el ) ;
2011-09-07 14:53:11 +00:00
this . form . on _created . add _last ( this . on _form _dialog _saved ) ;
this . form . on _saved . add _last ( this . on _form _dialog _saved ) ;
return this ;
} ,
2011-10-20 16:30:08 +00:00
select _id : function ( id ) {
if ( this . form . dataset . select _id ( id ) ) {
return this . form . do _show ( ) ;
} else {
this . do _warn ( "Could not find id in dataset" ) ;
return $ . Deferred ( ) . reject ( ) ;
}
2011-09-07 14:53:11 +00:00
} ,
2011-09-12 13:36:39 +00:00
on _form _dialog _saved : function ( r ) {
2011-09-07 14:53:11 +00:00
this . close ( ) ;
}
} ) ;
2011-07-26 21:00:05 +00:00
2012-04-17 12:09:49 +00:00
instance . web . form . compute _domain = function ( expr , fields ) {
2011-04-11 17:36:43 +00:00
var stack = [ ] ;
2011-04-11 17:50:25 +00:00
for ( var i = expr . length - 1 ; i >= 0 ; i -- ) {
2011-04-11 17:36:43 +00:00
var ex = expr [ i ] ;
if ( ex . length == 1 ) {
2011-04-11 17:50:25 +00:00
var top = stack . pop ( ) ;
2011-05-17 09:35:03 +00:00
switch ( ex ) {
2011-04-11 17:50:25 +00:00
case '|' :
stack . push ( stack . pop ( ) || top ) ;
continue ;
case '&' :
stack . push ( stack . pop ( ) && top ) ;
continue ;
case '!' :
stack . push ( ! top ) ;
continue ;
default :
2012-02-03 08:48:52 +00:00
throw new Error ( _ . str . sprintf (
_t ( "Unknown operator %s in domain %s" ) ,
ex , JSON . stringify ( expr ) ) ) ;
2011-04-11 17:50:25 +00:00
}
2011-04-11 17:36:43 +00:00
}
2011-08-10 15:42:38 +00:00
var field = fields [ ex [ 0 ] ] ;
if ( ! field ) {
2012-02-03 08:48:52 +00:00
throw new Error ( _ . str . sprintf (
_t ( "Unknown field %s in domain %s" ) ,
ex [ 0 ] , JSON . stringify ( expr ) ) ) ;
2011-08-10 15:42:38 +00:00
}
2012-04-18 09:16:13 +00:00
var field _value = field . get _value ? field . get _value ( ) : field . value ;
2011-04-11 17:36:43 +00:00
var op = ex [ 1 ] ;
var val = ex [ 2 ] ;
switch ( op . toLowerCase ( ) ) {
case '=' :
case '==' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value == val ) ;
2011-04-11 17:36:43 +00:00
break ;
case '!=' :
case '<>' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value != val ) ;
2011-04-11 17:36:43 +00:00
break ;
case '<' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value < val ) ;
2011-04-11 17:36:43 +00:00
break ;
case '>' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value > val ) ;
2011-04-11 17:36:43 +00:00
break ;
case '<=' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value <= val ) ;
2011-04-11 17:36:43 +00:00
break ;
case '>=' :
2011-08-10 15:42:38 +00:00
stack . push ( field _value >= val ) ;
2011-04-11 17:36:43 +00:00
break ;
case 'in' :
2012-01-16 13:25:54 +00:00
if ( ! _ . isArray ( val ) ) val = [ val ] ;
2011-08-10 15:42:38 +00:00
stack . push ( _ ( val ) . contains ( field _value ) ) ;
2011-04-11 17:36:43 +00:00
break ;
case 'not in' :
2012-01-16 13:25:54 +00:00
if ( ! _ . isArray ( val ) ) val = [ val ] ;
2011-08-10 15:42:38 +00:00
stack . push ( ! _ ( val ) . contains ( field _value ) ) ;
2011-04-11 17:36:43 +00:00
break ;
default :
2012-02-03 08:48:52 +00:00
console . warn (
_t ( "Unsupported operator %s in domain %s" ) ,
op , JSON . stringify ( expr ) ) ;
2011-04-11 17:36:43 +00:00
}
}
2011-07-14 14:29:58 +00:00
return _ . all ( stack , _ . identity ) ;
2011-05-17 09:35:03 +00:00
} ;
2011-05-11 14:49:10 +00:00
2012-03-26 16:29:34 +00:00
/ * *
2012-04-19 13:43:41 +00:00
* Must be applied over an class already possessing the PropertiesMixin .
2012-03-26 16:29:34 +00:00
*
2012-08-24 18:27:07 +00:00
* Apply the result of the "invisible" domain to this . $el .
2012-03-26 16:29:34 +00:00
* /
2012-04-17 12:09:49 +00:00
instance . web . form . InvisibilityChangerMixin = {
2012-03-26 16:29:34 +00:00
init : function ( field _manager , invisible _domain ) {
2012-06-27 10:01:51 +00:00
var self = this ;
this . _ic _field _manager = field _manager ;
2012-03-26 16:29:34 +00:00
this . _ic _invisible _modifier = invisible _domain ;
this . _ic _field _manager . on ( "view_content_has_changed" , this , function ( ) {
2012-06-27 10:01:51 +00:00
var result = self . _ic _invisible _modifier === undefined ? false :
instance . web . form . compute _domain (
self . _ic _invisible _modifier ,
self . _ic _field _manager . fields ) ;
self . set ( { "invisible" : result } ) ;
2012-03-26 16:29:34 +00:00
} ) ;
2012-05-02 14:23:32 +00:00
this . set ( { invisible : this . _ic _invisible _modifier === true , force _invisible : false } ) ;
var check = function ( ) {
2012-06-27 10:01:51 +00:00
if ( self . get ( "invisible" ) || self . get ( 'force_invisible' ) ) {
self . set ( { "effective_invisible" : true } ) ;
2012-05-02 14:23:32 +00:00
} else {
2012-06-27 10:01:51 +00:00
self . set ( { "effective_invisible" : false } ) ;
2012-05-02 14:23:32 +00:00
}
} ;
this . on ( 'change:invisible' , this , check ) ;
this . on ( 'change:force_invisible' , this , check ) ;
2012-06-27 10:01:51 +00:00
check . call ( this ) ;
2012-03-26 16:29:34 +00:00
} ,
start : function ( ) {
2012-06-05 16:16:10 +00:00
this . on ( "change:effective_invisible" , this , this . _check _visibility ) ;
this . _check _visibility ( ) ;
} ,
_check _visibility : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . toggleClass ( 'oe_form_invisible' , this . get ( "effective_invisible" ) ) ;
2012-03-26 16:29:34 +00:00
} ,
} ;
2012-06-21 23:56:41 +00:00
instance . web . form . InvisibilityChanger = instance . web . Class . extend ( instance . web . PropertiesMixin , instance . web . form . InvisibilityChangerMixin , {
2012-08-24 18:27:07 +00:00
init : function ( parent , field _manager , invisible _domain , $el ) {
2012-03-26 16:29:34 +00:00
this . setParent ( parent ) ;
2012-04-19 13:43:41 +00:00
instance . web . PropertiesMixin . init . call ( this ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . InvisibilityChangerMixin . init . call ( this , field _manager , invisible _domain ) ;
2012-08-24 18:27:07 +00:00
this . $el = $el ;
2012-03-26 16:29:34 +00:00
this . start ( ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2012-03-26 16:29:34 +00:00
2012-06-21 23:56:41 +00:00
instance . web . form . FormWidget = instance . web . Widget . extend ( instance . web . form . InvisibilityChangerMixin , {
2011-09-12 11:43:50 +00:00
/ * *
2012-04-26 15:28:00 +00:00
* @ constructs instance . web . form . FormWidget
2012-04-17 12:09:49 +00:00
* @ extends instance . web . Widget
2011-09-12 11:43:50 +00:00
*
* @ param view
* @ param node
* /
2011-03-30 14:00:48 +00:00
init : function ( view , node ) {
2012-03-26 16:29:34 +00:00
this . _super ( view ) ;
2011-03-30 14:00:48 +00:00
this . view = view ;
this . node = node ;
2011-07-07 10:37:40 +00:00
this . modifiers = JSON . parse ( this . node . attrs . modifiers || '{}' ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . InvisibilityChangerMixin . init . call ( this , view , this . modifiers . invisible ) ;
2011-08-31 12:45:38 +00:00
2012-03-26 14:41:29 +00:00
this . view . on ( "view_content_has_changed" , this , this . process _modifiers ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-29 09:55:24 +00:00
renderElement : function ( ) {
this . _super ( ) ;
2012-08-24 18:27:07 +00:00
this . $el . addClass ( this . node . attrs [ "class" ] || "" ) ;
2012-03-29 09:55:24 +00:00
} ,
2012-02-21 16:29:12 +00:00
destroy : function ( ) {
2012-02-09 13:59:39 +00:00
$ . fn . tipsy . clear ( ) ;
2012-03-26 16:29:34 +00:00
this . _super . apply ( this , arguments ) ;
2012-01-26 14:37:34 +00:00
} ,
2012-06-25 13:52:15 +00:00
/ * *
2012-06-28 10:11:00 +00:00
* Sets up blur / focus forwarding from DOM elements to a widget ( ` this ` ) .
*
* This method is an utility method that is meant to be called by child classes .
2012-06-20 11:15:27 +00:00
*
* @ param { jQuery } $e jQuery object of elements to bind focus / blur on
* /
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
setupFocus : function ( $e ) {
var self = this ;
2012-06-25 14:40:13 +00:00
$e . on ( {
focus : function ( ) { self . trigger ( 'focused' ) ; } ,
blur : function ( ) { self . trigger ( 'blurred' ) ; }
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
} ) ;
} ,
2011-07-07 10:37:40 +00:00
process _modifiers : function ( ) {
2012-04-17 12:09:49 +00:00
var compute _domain = instance . web . form . compute _domain ;
2012-03-20 15:51:11 +00:00
var to _set = { } ;
2011-07-07 10:37:40 +00:00
for ( var a in this . modifiers ) {
2012-06-27 10:01:51 +00:00
if ( ! this . modifiers . hasOwnProperty ( a ) ) { continue ; }
2012-03-26 16:29:34 +00:00
if ( ! _ . include ( [ "invisible" ] , a ) ) {
var val = compute _domain ( this . modifiers [ a ] , this . view . fields ) ;
to _set [ a ] = val ;
}
2011-03-30 14:00:48 +00:00
}
2012-03-20 15:51:11 +00:00
this . set ( to _set ) ;
2011-10-13 09:38:14 +00:00
} ,
2011-11-14 20:59:16 +00:00
do _attach _tooltip : function ( widget , trigger , options ) {
widget = widget || this ;
2012-08-24 18:27:07 +00:00
trigger = trigger || this . $el ;
2011-11-14 20:59:16 +00:00
options = _ . extend ( {
2012-01-26 13:24:41 +00:00
delayIn : 500 ,
delayOut : 0 ,
fade : true ,
title : function ( ) {
2012-03-15 16:21:07 +00:00
var template = widget . template + '.tooltip' ;
2011-11-14 20:59:16 +00:00
if ( ! QWeb . has _template ( template ) ) {
template = 'WidgetLabel.tooltip' ;
}
return QWeb . render ( template , {
2012-08-14 15:29:00 +00:00
debug : instance . session . debug ,
2011-11-14 20:59:16 +00:00
widget : widget
2012-01-26 13:24:41 +00:00
} ) } ,
2012-02-09 10:54:01 +00:00
gravity : $ . fn . tipsy . autoBounds ( 50 , 'nw' ) ,
2012-01-26 13:24:41 +00:00
html : true ,
opacity : 0.85 ,
trigger : 'hover'
2011-11-14 20:59:16 +00:00
} , options || { } ) ;
2012-04-23 11:09:07 +00:00
$ ( trigger ) . tipsy ( options ) ;
2011-11-14 20:59:16 +00:00
} ,
2012-01-03 17:17:13 +00:00
_build _view _fields _values : function ( blacklist ) {
2011-10-13 09:38:14 +00:00
var a _dataset = this . view . dataset ;
2012-01-03 17:17:13 +00:00
var fields _values = this . view . get _fields _values ( blacklist ) ;
2011-11-15 13:29:14 +00:00
var active _id = a _dataset . ids [ a _dataset . index ] ;
_ . extend ( fields _values , {
active _id : active _id || false ,
active _ids : active _id ? [ active _id ] : [ ] ,
active _model : a _dataset . model ,
2011-12-19 17:30:55 +00:00
parent : { }
2011-11-15 13:29:14 +00:00
} ) ;
2011-12-19 17:30:55 +00:00
if ( a _dataset . parent _view ) {
2012-07-16 11:13:24 +00:00
fields _values . parent = a _dataset . parent _view . get _fields _values ( [ a _dataset . child _name ] ) ;
2011-12-19 17:30:55 +00:00
}
2011-10-13 09:38:14 +00:00
return fields _values ;
} ,
2012-01-03 17:17:13 +00:00
_build _eval _context : function ( blacklist ) {
2011-10-13 09:38:14 +00:00
var a _dataset = this . view . dataset ;
2012-04-17 12:09:49 +00:00
return new instance . web . CompoundContext ( a _dataset . get _context ( ) , this . _build _view _fields _values ( blacklist ) ) ;
2011-10-13 09:38:14 +00:00
} ,
/ * *
* Builds a new context usable for operations related to fields by merging
* the fields 'context with the action' s context .
* /
2012-01-03 17:17:13 +00:00
build _context : function ( blacklist ) {
2012-01-10 14:20:55 +00:00
// only use the model's context if there is not context on the node
var v _context = this . node . attrs . context ;
if ( ! v _context ) {
v _context = ( this . field || { } ) . context || { } ;
2011-10-13 09:38:14 +00:00
}
2012-07-12 12:56:37 +00:00
2012-01-10 14:20:55 +00:00
if ( v _context . _ _ref || true ) { //TODO: remove true
2012-01-03 17:17:13 +00:00
var fields _values = this . _build _eval _context ( blacklist ) ;
2012-04-17 12:09:49 +00:00
v _context = new instance . web . CompoundContext ( v _context ) . set _eval _context ( fields _values ) ;
2011-10-13 09:38:14 +00:00
}
2012-01-10 14:20:55 +00:00
return v _context ;
2011-10-13 09:38:14 +00:00
} ,
build _domain : function ( ) {
var f _domain = this . field . domain || [ ] ;
var n _domain = this . node . attrs . domain || null ;
// if there is a domain on the node, overrides the model's domain
var final _domain = n _domain !== null ? n _domain : f _domain ;
2012-01-03 17:17:13 +00:00
if ( ! ( final _domain instanceof Array ) || true ) { //TODO: remove true
2011-10-13 09:38:14 +00:00
var fields _values = this . _build _eval _context ( ) ;
2012-04-17 12:09:49 +00:00
final _domain = new instance . web . CompoundDomain ( final _domain ) . set _eval _context ( fields _values ) ;
2011-10-13 09:38:14 +00:00
}
return final _domain ;
2011-03-30 14:00:48 +00:00
}
2012-06-21 23:56:41 +00:00
} ) ;
2011-07-11 14:48:51 +00:00
2012-04-26 15:28:00 +00:00
instance . web . form . WidgetButton = instance . web . form . FormWidget . extend ( {
2012-03-13 15:58:52 +00:00
template : 'WidgetButton' ,
2011-03-30 14:00:48 +00:00
init : function ( view , node ) {
this . _super ( view , node ) ;
2011-09-30 16:15:06 +00:00
this . force _disabled = false ;
2012-03-14 17:09:49 +00:00
this . string = ( this . node . attrs . string || '' ) . replace ( /_/g , '' ) ;
2012-07-26 15:47:16 +00:00
if ( JSON . parse ( this . node . attrs . default _focus || "0" ) ) {
2011-06-20 18:25:49 +00:00
// TODO fme: provide enter key binding to widgets
this . view . default _focus _button = this ;
}
2012-07-26 00:58:35 +00:00
if ( this . node . attrs . icon && ( ! /\// . test ( this . node . attrs . icon ) ) ) {
this . node . attrs . icon = '/web/static/src/img/icons/' + this . node . attrs . icon + '.png' ;
}
2012-04-18 12:20:39 +00:00
this . view . on ( 'view_content_has_changed' , this , this . check _disable ) ;
2011-04-11 15:59:46 +00:00
} ,
start : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $el . click ( this . on _click ) ;
2012-08-14 15:29:00 +00:00
if ( this . node . attrs . help || instance . session . debug ) {
2011-11-14 20:59:16 +00:00
this . do _attach _tooltip ( ) ;
}
2012-08-24 18:27:07 +00:00
this . setupFocus ( this . $el ) ;
2011-04-11 15:59:46 +00:00
} ,
2011-09-26 12:39:29 +00:00
on _click : function ( ) {
2011-09-30 16:15:06 +00:00
var self = this ;
this . force _disabled = true ;
this . check _disable ( ) ;
this . execute _action ( ) . always ( function ( ) {
self . force _disabled = false ;
self . check _disable ( ) ;
} ) ;
} ,
execute _action : function ( ) {
2011-04-11 15:59:46 +00:00
var self = this ;
2011-09-26 12:39:29 +00:00
var exec _action = function ( ) {
if ( self . node . attrs . confirm ) {
2011-09-30 16:15:06 +00:00
var def = $ . Deferred ( ) ;
2012-04-17 12:09:49 +00:00
var dialog = instance . web . dialog ( $ ( '<div/>' ) . text ( self . node . attrs . confirm ) , {
2011-12-15 10:06:52 +00:00
title : _t ( 'Confirm' ) ,
2011-04-11 15:59:46 +00:00
modal : true ,
2011-12-15 10:40:31 +00:00
buttons : [
2012-01-17 15:33:33 +00:00
{ text : _t ( "Cancel" ) , click : function ( ) {
def . resolve ( ) ;
$ ( this ) . dialog ( "close" ) ;
}
} ,
2011-12-15 10:40:31 +00:00
{ text : _t ( "Ok" ) , click : function ( ) {
self . on _confirmed ( ) . then ( function ( ) {
def . resolve ( ) ;
} ) ;
$ ( this ) . dialog ( "close" ) ;
}
2011-04-11 15:59:46 +00:00
}
2011-12-15 10:40:31 +00:00
]
2011-04-11 15:59:46 +00:00
} ) ;
2011-09-30 16:15:06 +00:00
return def . promise ( ) ;
2011-04-11 15:59:46 +00:00
} else {
2011-09-30 16:15:06 +00:00
return self . on _confirmed ( ) ;
2011-04-11 15:59:46 +00:00
}
2011-09-26 12:39:29 +00:00
} ;
2011-10-12 14:10:07 +00:00
if ( ! this . node . attrs . special ) {
2012-04-18 15:18:44 +00:00
this . view . force _dirty = true ;
2011-09-30 16:15:06 +00:00
return this . view . recursive _save ( ) . pipe ( exec _action ) ;
2011-09-26 12:39:29 +00:00
} else {
2011-09-30 16:15:06 +00:00
return exec _action ( ) ;
2011-04-11 15:59:46 +00:00
}
} ,
on _confirmed : function ( ) {
2011-05-04 09:12:33 +00:00
var self = this ;
2011-10-19 12:50:34 +00:00
2012-07-23 13:49:14 +00:00
var context = this . build _context ( ) ;
2011-05-04 09:12:33 +00:00
2011-09-30 16:15:06 +00:00
return this . view . do _execute _action (
2011-10-13 09:38:14 +00:00
_ . extend ( { } , this . node . attrs , { context : context } ) ,
this . view . dataset , this . view . datarecord . id , function ( ) {
2011-06-28 10:30:49 +00:00
self . view . reload ( ) ;
2011-04-12 15:09:55 +00:00
} ) ;
2011-09-30 15:46:12 +00:00
} ,
2011-09-30 16:15:06 +00:00
check _disable : function ( ) {
2012-03-20 15:51:11 +00:00
var disabled = ( this . force _disabled || ! this . view . is _interactible _record ( ) ) ;
2012-08-24 18:27:07 +00:00
this . $el . prop ( 'disabled' , disabled ) ;
this . $el . css ( 'color' , disabled ? 'grey' : '' ) ;
2011-03-30 14:00:48 +00:00
}
} ) ;
2012-03-13 16:37:19 +00:00
/ * *
* Interface to be implemented by fields .
2012-07-12 12:56:37 +00:00
*
2012-03-15 16:43:26 +00:00
* Properties :
* - readonly : boolean . If set to true the field should appear in readonly mode .
2012-03-20 16:18:06 +00:00
* - force _readonly : boolean , When it is true , the field should always appear
* in read only mode , no matter what the value of the "readonly" property can be .
2012-03-15 16:43:26 +00:00
* Events :
2012-04-18 13:05:40 +00:00
* - changed _value : triggered to inform the view to check on _changes
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* /
2012-06-21 23:56:41 +00:00
instance . web . form . FieldInterface = {
2012-03-15 16:43:26 +00:00
/ * *
* Constructor takes 2 arguments :
2012-05-02 09:18:47 +00:00
* - field _manager : Implements FieldManagerMixin
2012-03-15 16:43:26 +00:00
* - node : the "<field>" node in json form
* /
init : function ( field _manager , node ) { } ,
2012-03-13 16:37:19 +00:00
/ * *
* Called by the form view to indicate the value of the field .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* set _value ( ) may return an object that can be passed to $ . when ( ) that represents the moment when
* the field has finished all operations necessary before the user can effectively use the widget .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* Multiple calls to set _value ( ) can occur at any time and must be handled correctly by the implementation ,
* regardless of any asynchronous operation currently running and the status of any promise that a
* previous call to set _value ( ) could have returned .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* set _value ( ) must be able , at any moment , to handle the syntax returned by the "read" method of the
* osv class in the OpenERP server as well as the syntax used by the set _value ( ) ( see below ) . It must
* also be able to handle any other format commonly used in the _defaults key on the models in the addons
* as well as any format commonly returned in a on _change . It must be able to autodetect those formats as
* no information is ever given to know which format is used .
* /
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) { } ,
2012-03-13 16:37:19 +00:00
/ * *
* Get the current value of the widget .
2012-07-12 12:56:37 +00:00
*
2012-06-27 10:01:51 +00:00
* Must always return a syntactically correct value to be passed to the "write" method of the osv class in
2012-03-13 16:37:19 +00:00
* the OpenERP server , although it is not assumed to respect the constraints applied to the field .
2012-06-27 10:01:51 +00:00
* For example if the field is marked as "required" , a call to get _value ( ) can return false .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* get _value ( ) can also be called * before * a call to set _value ( ) and , in that case , is supposed to
2012-06-27 10:01:51 +00:00
* return a default value according to the type of field .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* This method is always assumed to perform synchronously , it can not return a promise .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* If there was no user interaction to modify the value of the field , it is always assumed that
* get _value ( ) return the same semantic value than the one passed in the last call to set _value ( ) ,
2012-06-27 10:01:51 +00:00
* although the syntax can be different . This can be the case for type of fields that have a different
2012-03-13 16:37:19 +00:00
* syntax for "read" and "write" ( example : m2o : set _value ( [ 0 , "Administrator" ] ) , get _value ( ) => 0 ) .
* /
get _value : function ( ) { } ,
2012-03-27 12:47:04 +00:00
/ * *
* Inform the current object of the id it should use to match a html < label > that exists somewhere in the
* view .
* /
2012-04-18 11:50:04 +00:00
set _input _id : function ( id ) { } ,
2012-04-18 13:05:40 +00:00
/ * *
* Returns true if is _syntax _valid ( ) returns true and the value is semantically
* valid too according to the semantic restrictions applied to the field .
* /
2012-04-18 11:50:04 +00:00
is _valid : function ( ) { } ,
2012-04-18 13:05:40 +00:00
/ * *
2012-06-27 10:01:51 +00:00
* Returns true if the field holds a value which is syntactically correct , ignoring
2012-04-18 13:05:40 +00:00
* the potential semantic restrictions applied to the field .
* /
2012-04-18 11:50:04 +00:00
is _syntax _valid : function ( ) { } ,
2012-04-18 13:05:40 +00:00
/ * *
2012-06-28 13:02:49 +00:00
* Must set the focus on the field . Return false if field is not focusable .
2012-04-18 13:05:40 +00:00
* /
2012-04-18 12:37:04 +00:00
focus : function ( ) { } ,
2012-08-30 16:12:37 +00:00
/ * *
* Called when the translate button is clicked .
* /
on _translate : function ( ) { } ,
2012-03-13 16:37:19 +00:00
} ;
/ * *
2012-06-21 23:56:41 +00:00
* Abstract class for classes implementing FieldInterface .
2012-07-12 12:56:37 +00:00
*
2012-03-15 16:43:26 +00:00
* Properties :
* - effective _readonly : when it is true , the widget is displayed as readonly . Vary depending
* the values of the "readonly" property and the "force_readonly" property on the field manager .
2012-04-18 13:05:40 +00:00
* - value : useful property to hold the value of the field . By default , set _value ( ) and get _value ( )
* set and retrieve the value property . Changing the value property also triggers automatically
* a 'changed_value' event that inform the view to trigger on _changes .
2012-07-12 12:56:37 +00:00
*
2012-03-13 16:37:19 +00:00
* /
2012-06-21 23:56:41 +00:00
instance . web . form . AbstractField = instance . web . form . FormWidget . extend ( instance . web . form . FieldInterface , {
2011-09-12 11:43:50 +00:00
/ * *
2012-04-17 12:09:49 +00:00
* @ constructs instance . web . form . AbstractField
2012-04-26 15:28:00 +00:00
* @ extends instance . web . form . FormWidget
2011-09-12 11:43:50 +00:00
*
2012-03-15 16:43:26 +00:00
* @ param field _manager
2011-09-12 11:43:50 +00:00
* @ param node
* /
2012-03-15 16:43:26 +00:00
init : function ( field _manager , node ) {
2012-06-27 10:01:51 +00:00
var self = this
2012-03-15 16:43:26 +00:00
this . _super ( field _manager , node ) ;
2012-04-17 13:33:07 +00:00
this . field _manager = field _manager ;
2012-03-14 17:09:49 +00:00
this . name = this . node . attrs . name ;
2012-04-17 13:33:07 +00:00
this . field = this . field _manager . get _field ( this . name ) ;
2012-06-27 15:17:59 +00:00
this . widget = this . node . attrs . widget ;
2012-06-29 15:02:25 +00:00
this . string = this . node . attrs . string || this . field . string || this . name ;
2012-08-18 20:16:32 +00:00
this . options = JSON . parse ( this . node . attrs . options || '{}' ) ;
2012-06-27 15:17:59 +00:00
this . set ( { 'value' : false } ) ;
2012-03-27 13:02:54 +00:00
this . set ( { required : this . modifiers [ 'required' ] === true } ) ;
2012-07-12 12:56:37 +00:00
2012-03-15 16:43:26 +00:00
// some events to make the property "effective_readonly" sync automatically with "readonly" and
// "force_readonly"
this . set ( { "readonly" : this . modifiers [ 'readonly' ] === true } ) ;
2012-07-20 14:13:04 +00:00
this . set ( { "force_readonly" : false } ) ;
2012-03-15 16:43:26 +00:00
var test _effective _readonly = function ( ) {
2012-06-27 10:01:51 +00:00
self . set ( { "effective_readonly" : self . get ( "readonly" ) || ! ! self . get ( "force_readonly" ) } ) ;
2012-03-15 16:43:26 +00:00
} ;
2012-03-20 09:58:19 +00:00
this . on ( "change:readonly" , this , test _effective _readonly ) ;
2012-03-20 16:18:06 +00:00
this . on ( "change:force_readonly" , this , test _effective _readonly ) ;
2012-06-27 10:01:51 +00:00
test _effective _readonly . call ( this ) ;
2012-04-17 15:45:05 +00:00
this . on ( "change:value" , this , function ( ) {
2012-04-18 09:57:58 +00:00
if ( ! this . _inhibit _on _change )
2012-04-18 12:23:41 +00:00
this . trigger ( 'changed_value' ) ;
this . _check _css _flags ( ) ;
2012-04-18 12:20:39 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-05-02 11:42:15 +00:00
renderElement : function ( ) {
2012-05-09 12:56:42 +00:00
var self = this ;
2012-05-02 11:42:15 +00:00
this . _super ( ) ;
2011-08-24 15:13:57 +00:00
if ( this . field . translate ) {
2012-08-24 18:27:07 +00:00
this . $el . addClass ( 'oe_form_field_translatable' ) ;
2012-08-30 16:12:37 +00:00
this . $el . find ( '.oe_field_translate' ) . click ( this . on _translate ) ;
2011-08-24 15:13:57 +00:00
}
2012-08-24 18:27:07 +00:00
this . $label = this . view . $el . find ( 'label[for=' + this . id _for _label + ']' ) ;
2012-08-14 15:29:00 +00:00
if ( instance . session . debug ) {
2012-08-24 18:27:07 +00:00
this . do _attach _tooltip ( this , this . $label [ 0 ] || this . $el ) ;
2012-05-09 12:56:42 +00:00
this . $label . off ( 'dblclick' ) . on ( 'dblclick' , function ( ) {
console . log ( "Field '%s' of type '%s' in View: %o" , self . name , ( self . node . attrs . widget || self . field . type ) , self . view ) ;
window . w = self ;
console . log ( "window.w =" , window . w ) ;
} ) ;
2011-11-14 20:59:16 +00:00
}
2012-03-27 13:02:54 +00:00
if ( ! this . disable _utility _classes ) {
2012-05-02 11:42:15 +00:00
this . off ( "change:required" , this , this . _set _required ) ;
this . on ( "change:required" , this , this . _set _required ) ;
this . _set _required ( ) ;
2012-03-27 13:02:54 +00:00
}
2012-06-05 16:16:10 +00:00
this . _check _visibility ( ) ;
this . _check _css _flags ( ) ;
2011-08-24 15:13:57 +00:00
} ,
2012-05-02 11:42:15 +00:00
/ * *
* Private . Do not use .
* /
_set _required : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . toggleClass ( 'oe_form_required' , this . get ( "required" ) ) ;
2012-05-02 11:42:15 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2012-04-17 15:45:05 +00:00
this . _inhibit _on _change = true ;
2012-04-17 14:32:27 +00:00
this . set ( { 'value' : value _ } ) ;
2012-04-17 15:45:05 +00:00
this . _inhibit _on _change = false ;
2011-08-24 15:13:57 +00:00
} ,
2011-04-05 14:34:25 +00:00
get _value : function ( ) {
2012-04-17 14:32:27 +00:00
return this . get ( 'value' ) ;
2011-03-30 14:00:48 +00:00
} ,
2011-07-27 10:25:19 +00:00
is _valid : function ( ) {
2012-06-25 13:52:15 +00:00
return this . is _syntax _valid ( ) && ! ( this . get ( 'required' ) && this . is _false ( ) ) ;
2011-07-27 10:25:19 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2012-04-18 09:16:13 +00:00
return true ;
2011-07-27 10:25:19 +00:00
} ,
2012-04-18 13:05:40 +00:00
/ * *
* Method useful to implement to ease validity testing . Must return true if the current
* value is similar to false in OpenERP .
* /
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
return this . get ( 'value' ) === false ;
2011-06-29 16:21:36 +00:00
} ,
2012-06-28 14:36:16 +00:00
_check _css _flags : function ( ) {
2011-08-29 14:08:39 +00:00
if ( this . field . translate ) {
2012-08-24 18:27:07 +00:00
this . $el . find ( '.oe_field_translate' ) . toggle ( ! this . field _manager . is _create _mode ( ) ) ;
2011-08-29 14:08:39 +00:00
}
2011-07-27 09:09:05 +00:00
if ( ! this . disable _utility _classes ) {
2012-04-18 12:20:39 +00:00
if ( this . field _manager . get ( 'display_invalid_fields' ) ) {
2012-08-24 18:27:07 +00:00
this . $el . toggleClass ( 'oe_form_invalid' , ! this . is _valid ( ) ) ;
2011-07-27 09:09:05 +00:00
}
2011-04-11 11:35:16 +00:00
}
2011-03-30 14:00:48 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-01 16:49:33 +00:00
return false ;
2011-12-21 15:08:31 +00:00
} ,
2012-03-27 12:47:04 +00:00
set _input _id : function ( id ) {
this . id _for _label = id ;
} ,
2012-08-30 16:12:37 +00:00
on _translate : function ( ) {
var self = this ;
2012-09-04 13:31:37 +00:00
var trans = new instance . web . DataSet ( this , 'ir.translation' ) ;
return trans . call _button ( 'translate_fields' , [ this . view . dataset . model , this . view . datarecord . id , this . name , this . view . dataset . get _context ( ) ] ) . then ( function ( r ) {
self . do _action ( r . result ) ;
2012-08-30 16:12:37 +00:00
} ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
2012-03-19 10:31:16 +00:00
/ * *
* A mixin to apply on any field that has to completely re - render when its readonly state
* switch .
* /
2012-04-17 12:09:49 +00:00
instance . web . form . ReinitializeFieldMixin = {
2012-03-20 10:17:47 +00:00
/ * *
* Default implementation of start ( ) , use it or call explicitly initialize _field ( ) .
* /
2011-03-30 14:00:48 +00:00
start : function ( ) {
2012-03-20 10:17:47 +00:00
this . _super ( ) ;
this . initialize _field ( ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-20 10:17:47 +00:00
initialize _field : function ( ) {
2012-03-16 11:22:23 +00:00
this . on ( "change:effective_readonly" , this , function ( ) {
2012-03-20 10:17:47 +00:00
this . destroy _content ( ) ;
2012-03-19 10:06:39 +00:00
this . renderElement ( ) ;
2012-03-20 10:17:47 +00:00
this . initialize _content ( ) ;
2012-03-16 11:22:23 +00:00
this . render _value ( ) ;
} ) ;
2012-03-20 10:17:47 +00:00
this . initialize _content ( ) ;
2012-03-23 15:04:48 +00:00
this . render _value ( ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-20 10:17:47 +00:00
/ * *
* Called to destroy anything that could have been created previously , called before a
* re - initialization .
* /
destroy _content : function ( ) { } ,
/ * *
* Called to initialize the content .
* /
initialize _content : function ( ) { } ,
/ * *
* Called to render the value . Should also be explicitly called at the end of a set _value ( ) .
* /
2012-03-19 10:31:16 +00:00
render _value : function ( ) { } ,
} ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldChar = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-03-19 10:31:16 +00:00
template : 'FieldChar' ,
2012-06-01 17:10:53 +00:00
widget _class : 'oe_form_field_char' ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2012-03-19 10:31:16 +00:00
this . password = this . node . attrs . password === 'True' || this . node . attrs . password === '1' ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-04-17 15:45:05 +00:00
var self = this ;
2012-08-24 18:27:07 +00:00
var $input = this . $el . find ( 'input' ) ;
2012-06-25 15:24:45 +00:00
$input . change ( function ( ) {
self . set ( { 'value' : instance . web . parse _value ( $input . val ( ) , self ) } ) ;
2012-04-17 15:45:05 +00:00
} ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
this . setupFocus ( $input ) ;
2011-04-11 11:35:16 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2012-04-17 15:45:05 +00:00
this . _super ( value _ ) ;
2012-03-16 11:22:23 +00:00
this . render _value ( ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-16 11:22:23 +00:00
render _value : function ( ) {
2012-04-17 14:32:27 +00:00
var show _value = instance . web . format _value ( this . get ( 'value' ) , this , '' ) ;
2012-03-16 11:22:23 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'input' ) . val ( show _value ) ;
2012-03-16 11:22:23 +00:00
} else {
if ( this . password ) {
show _value = new Array ( show _value . length + 1 ) . join ( '*' ) ;
}
2012-08-24 18:27:07 +00:00
this . $el . text ( show _value ) ;
2011-04-11 11:35:16 +00:00
}
2011-06-20 18:25:49 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2012-03-19 15:34:06 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
try {
2012-08-24 18:27:07 +00:00
var value _ = instance . web . parse _value ( this . $el . find ( 'input' ) . val ( ) , this , '' ) ;
2012-04-18 09:50:43 +00:00
return true ;
2012-03-19 15:34:06 +00:00
} catch ( e ) {
2012-04-18 09:16:13 +00:00
return false ;
2012-03-19 15:34:06 +00:00
}
2011-04-11 11:35:16 +00:00
}
2012-04-18 09:16:13 +00:00
return true ;
2011-06-20 18:25:49 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
2012-06-07 14:31:04 +00:00
return this . get ( 'value' ) === '' || this . _super ( ) ;
2012-04-18 09:50:43 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-08 15:41:26 +00:00
this . $ ( 'input:first' ) . focus ( ) ;
2011-03-30 14:00:48 +00:00
}
2012-06-21 23:56:41 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
2012-04-17 12:09:49 +00:00
instance . web . form . FieldID = instance . web . form . FieldChar . extend ( {
2012-07-12 12:56:37 +00:00
2012-01-25 14:04:38 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldEmail = instance . web . form . FieldChar . extend ( {
2012-03-13 20:28:23 +00:00
template : 'FieldEmail' ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-03-16 11:33:22 +00:00
this . _super ( ) ;
2012-08-24 18:27:07 +00:00
var $button = this . $el . find ( 'button' ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
$button . click ( this . on _button _clicked ) ;
this . setupFocus ( $button ) ;
2011-05-12 16:25:50 +00:00
} ,
2012-03-16 11:33:22 +00:00
render _value : function ( ) {
if ( ! this . get ( "effective_readonly" ) ) {
this . _super ( ) ;
} else {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'a' )
2012-04-17 14:32:27 +00:00
. attr ( 'href' , 'mailto:' + this . get ( 'value' ) )
2012-06-27 15:29:22 +00:00
. text ( this . get ( 'value' ) || '' ) ;
2012-03-16 11:33:22 +00:00
}
} ,
2011-05-12 16:25:50 +00:00
on _button _clicked : function ( ) {
2012-04-18 09:50:43 +00:00
if ( ! this . get ( 'value' ) || ! this . is _syntax _valid ( ) ) {
2011-10-13 10:26:53 +00:00
this . do _warn ( "E-mail error" , "Can't send email to invalid e-mail address" ) ;
2011-05-12 16:25:50 +00:00
} else {
2012-04-17 14:32:27 +00:00
location . href = 'mailto:' + this . get ( 'value' ) ;
2011-05-12 16:25:50 +00:00
}
2011-04-11 11:35:16 +00:00
}
2011-03-30 14:00:48 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldUrl = instance . web . form . FieldChar . extend ( {
2012-03-13 20:28:23 +00:00
template : 'FieldUrl' ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-03-16 11:44:24 +00:00
this . _super ( ) ;
2012-08-24 18:27:07 +00:00
var $button = this . $el . find ( 'button' ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
$button . click ( this . on _button _clicked ) ;
this . setupFocus ( $button ) ;
2011-05-19 13:55:22 +00:00
} ,
2012-03-16 11:44:24 +00:00
render _value : function ( ) {
if ( ! this . get ( "effective_readonly" ) ) {
this . _super ( ) ;
} else {
2012-04-17 14:32:27 +00:00
var tmp = this . get ( 'value' ) ;
2012-03-16 11:44:24 +00:00
var s = /(\w+):(.+)/ . exec ( tmp ) ;
if ( ! s ) {
2012-04-17 14:32:27 +00:00
tmp = "http://" + this . get ( 'value' ) ;
2012-03-16 11:44:24 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . find ( 'a' ) . attr ( 'href' , tmp ) . text ( this . get ( 'value' ) ? tmp : '' ) ;
2012-03-16 11:44:24 +00:00
}
} ,
2011-05-19 13:55:22 +00:00
on _button _clicked : function ( ) {
2012-04-17 14:32:27 +00:00
if ( ! this . get ( 'value' ) ) {
2011-10-13 10:26:53 +00:00
this . do _warn ( "Resource error" , "This resource is empty" ) ;
2011-05-19 13:55:22 +00:00
} else {
2012-04-17 14:32:27 +00:00
var url = $ . trim ( this . get ( 'value' ) ) ;
2012-04-16 23:32:07 +00:00
if ( /^www\./i . test ( url ) )
url = 'http://' + url ;
window . open ( url ) ;
2011-05-19 13:55:22 +00:00
}
}
2011-03-30 14:00:48 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldFloat = instance . web . form . FieldChar . extend ( {
2012-03-29 10:20:48 +00:00
is _field _number : true ,
2012-06-01 17:10:53 +00:00
widget _class : 'oe_form_field_float' ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2012-04-17 14:32:27 +00:00
this . set ( { 'value' : 0 } ) ;
2012-03-14 17:09:49 +00:00
if ( this . node . attrs . digits ) {
2012-06-21 12:41:23 +00:00
this . digits = this . node . attrs . digits ;
2011-10-21 12:22:56 +00:00
} else {
2012-03-13 20:28:23 +00:00
this . digits = this . field . digits ;
2011-10-21 12:22:56 +00:00
}
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
if ( value _ === false || value _ === undefined ) {
2011-05-09 15:19:23 +00:00
// As in GTK client, floats default to 0
2012-04-17 14:32:27 +00:00
value _ = 0 ;
2011-05-09 15:19:23 +00:00
}
2012-04-17 14:32:27 +00:00
this . _super . apply ( this , [ value _ ] ) ;
2012-08-08 15:41:26 +00:00
} ,
focus : function ( ) {
this . $ ( 'input:first' ) . select ( ) ;
2011-07-07 08:55:15 +00:00
}
} ) ;
2012-08-14 13:42:46 +00:00
instance . web . DateTimeWidget = instance . web . Widget . extend ( {
2012-06-04 09:32:39 +00:00
template : "web.datepicker" ,
2011-09-21 10:41:36 +00:00
jqueryui _object : 'datetimepicker' ,
type _of _date : "datetime" ,
2011-11-16 22:27:05 +00:00
init : function ( parent ) {
this . _super ( parent ) ;
this . name = parent . name ;
} ,
2011-04-05 14:34:25 +00:00
start : function ( ) {
2011-09-14 15:46:10 +00:00
var self = this ;
2012-08-24 18:27:07 +00:00
this . $input = this . $el . find ( 'input.oe_datepicker_master' ) ;
this . $input _picker = this . $el . find ( 'input.oe_datepicker_container' ) ;
2011-11-16 22:27:05 +00:00
this . $input . change ( this . on _change ) ;
2011-09-14 15:46:10 +00:00
this . picker ( {
2012-05-23 13:15:27 +00:00
onClose : this . on _picker _select ,
2011-09-14 15:46:10 +00:00
onSelect : this . on _picker _select ,
changeMonth : true ,
changeYear : true ,
showWeek : true ,
2012-07-23 13:56:20 +00:00
showButtonPanel : true ,
firstDay : Date . CultureInfo . firstDayOfWeek
2011-09-14 15:46:10 +00:00
} ) ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'img.oe_datepicker_trigger' ) . click ( function ( ) {
2012-06-25 13:52:15 +00:00
if ( self . get ( "effective_readonly" ) || self . picker ( 'widget' ) . is ( ':visible' ) ) {
2012-06-20 11:34:02 +00:00
self . $input . focus ( ) ;
return ;
2011-09-14 15:46:10 +00:00
}
2012-06-25 13:52:15 +00:00
self . picker ( 'setDate' , self . value ? instance . web . auto _str _to _date ( self . value ) : new Date ( ) ) ;
2012-06-20 11:34:02 +00:00
self . $input _picker . show ( ) ;
self . picker ( 'show' ) ;
self . $input _picker . hide ( ) ;
2011-09-14 15:46:10 +00:00
} ) ;
2011-09-21 11:50:54 +00:00
this . set _readonly ( false ) ;
2012-04-17 14:32:27 +00:00
this . set ( { 'value' : false } ) ;
2011-05-12 10:36:01 +00:00
} ,
2011-09-14 15:46:10 +00:00
picker : function ( ) {
2011-11-16 22:27:05 +00:00
return $ . fn [ this . jqueryui _object ] . apply ( this . $input _picker , arguments ) ;
2011-09-14 15:46:10 +00:00
} ,
2012-04-17 11:43:58 +00:00
on _picker _select : function ( text , instance _ ) {
2011-09-14 15:46:10 +00:00
var date = this . picker ( 'getDate' ) ;
2012-06-20 11:34:02 +00:00
this . $input
. val ( date ? this . format _client ( date ) : '' )
. change ( )
. focus ( ) ;
2011-09-14 15:46:10 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
this . set ( { 'value' : value _ } ) ;
this . $input . val ( value _ ? this . format _client ( value _ ) : '' ) ;
2011-09-14 15:46:10 +00:00
} ,
get _value : function ( ) {
2012-04-17 14:32:27 +00:00
return this . get ( 'value' ) ;
2011-05-12 10:36:01 +00:00
} ,
2012-04-17 15:45:05 +00:00
set _value _from _ui _ : function ( ) {
2012-04-17 14:32:27 +00:00
var value _ = this . $input . val ( ) || false ;
this . set ( { 'value' : this . parse _client ( value _ ) } ) ;
2011-05-12 10:36:01 +00:00
} ,
2011-09-21 10:41:36 +00:00
set _readonly : function ( readonly ) {
this . readonly = readonly ;
2012-02-13 16:31:20 +00:00
this . $input . prop ( 'readonly' , this . readonly ) ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'img.oe_datepicker_trigger' ) . toggleClass ( 'oe_input_icon_disabled' , readonly ) ;
2011-06-30 09:40:53 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _valid _ : function ( ) {
2012-04-17 14:32:27 +00:00
var value _ = this . $input . val ( ) ;
if ( value _ === "" ) {
2012-03-26 14:31:12 +00:00
return true ;
2011-06-30 12:10:08 +00:00
} else {
2011-09-14 15:46:10 +00:00
try {
2012-04-17 14:32:27 +00:00
this . parse _client ( value _ ) ;
2011-09-21 10:41:36 +00:00
return true ;
2011-09-14 15:46:10 +00:00
} catch ( e ) {
2011-09-21 10:41:36 +00:00
return false ;
2011-09-14 15:46:10 +00:00
}
2011-06-30 12:10:08 +00:00
}
2011-05-12 10:36:01 +00:00
} ,
2011-09-14 15:46:10 +00:00
parse _client : function ( v ) {
2012-04-17 12:09:49 +00:00
return instance . web . parse _value ( v , { "widget" : this . type _of _date } ) ;
2011-09-14 15:46:10 +00:00
} ,
format _client : function ( v ) {
2012-04-17 12:09:49 +00:00
return instance . web . format _value ( v , { "widget" : this . type _of _date } ) ;
2011-09-21 10:41:36 +00:00
} ,
on _change : function ( ) {
2012-04-18 09:50:43 +00:00
if ( this . is _valid _ ( ) ) {
2012-04-17 15:45:05 +00:00
this . set _value _from _ui _ ( ) ;
2011-09-21 10:41:36 +00:00
}
2011-09-01 15:22:24 +00:00
}
2011-03-30 14:00:48 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . DateWidget = instance . web . DateTimeWidget . extend ( {
2011-09-21 10:41:36 +00:00
jqueryui _object : 'datepicker' ,
2011-11-16 22:27:05 +00:00
type _of _date : "date"
2011-03-30 14:00:48 +00:00
} ) ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldDatetime = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-06-04 09:32:39 +00:00
template : "FieldDatetime" ,
2011-09-21 10:41:36 +00:00
build _widget : function ( ) {
2012-04-17 12:09:49 +00:00
return new instance . web . DateTimeWidget ( this ) ;
2011-09-21 10:41:36 +00:00
} ,
2012-03-20 10:17:47 +00:00
destroy _content : function ( ) {
2012-03-19 15:10:45 +00:00
if ( this . datewidget ) {
2012-03-20 09:58:19 +00:00
this . datewidget . destroy ( ) ;
2012-03-19 15:10:45 +00:00
this . datewidget = undefined ;
}
2011-09-21 10:41:36 +00:00
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-03-19 15:10:45 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
this . datewidget = this . build _widget ( ) ;
2012-04-17 15:45:05 +00:00
this . datewidget . on _change . add _last ( _ . bind ( function ( ) {
this . set ( { 'value' : this . datewidget . get _value ( ) } ) ;
} , this ) ) ;
2012-08-24 18:27:07 +00:00
this . datewidget . appendTo ( this . $el ) ;
2012-06-25 13:52:15 +00:00
this . setupFocus ( this . datewidget . $input ) ;
2012-03-19 15:10:45 +00:00
}
2011-09-21 10:41:36 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
this . _super ( value _ ) ;
2012-03-19 15:10:45 +00:00
this . render _value ( ) ;
2011-09-21 10:41:36 +00:00
} ,
2012-03-19 15:10:45 +00:00
render _value : function ( ) {
if ( ! this . get ( "effective_readonly" ) ) {
2012-04-17 14:32:27 +00:00
this . datewidget . set _value ( this . get ( 'value' ) ) ;
2012-03-19 15:10:45 +00:00
} else {
2012-08-24 18:27:07 +00:00
this . $el . text ( instance . web . format _value ( this . get ( 'value' ) , this , '' ) ) ;
2012-03-19 15:10:45 +00:00
}
2011-09-21 10:41:36 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2012-03-26 14:31:12 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
2012-04-18 09:50:43 +00:00
return this . datewidget . is _valid _ ( ) ;
2012-03-26 14:31:12 +00:00
}
2012-04-18 09:16:13 +00:00
return true ;
2011-09-21 10:41:36 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
2012-06-07 14:31:04 +00:00
return this . get ( 'value' ) === '' || this . _super ( ) ;
2011-09-21 10:41:36 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-01 16:49:33 +00:00
if ( this . datewidget && this . datewidget . $input ) {
this . datewidget . $input . focus ( ) ;
}
2011-09-21 10:41:36 +00:00
}
2012-06-21 23:56:41 +00:00
} ) ;
2011-09-21 10:41:36 +00:00
2012-04-17 12:09:49 +00:00
instance . web . form . FieldDate = instance . web . form . FieldDatetime . extend ( {
2012-06-04 09:32:39 +00:00
template : "FieldDate" ,
2011-09-21 10:41:36 +00:00
build _widget : function ( ) {
2012-04-17 12:09:49 +00:00
return new instance . web . DateWidget ( this ) ;
2011-09-21 10:41:36 +00:00
}
} ) ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldText = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-03-13 20:28:23 +00:00
template : 'FieldText' ,
2012-08-31 10:50:42 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-08-31 10:50:42 +00:00
var self = this ;
2012-08-24 18:27:07 +00:00
this . $textarea = this . $el . find ( 'textarea' ) ;
2012-08-30 07:20:24 +00:00
this . default _height = this . $textarea . css ( 'height' ) ;
2012-03-19 15:18:23 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
2012-04-17 15:45:05 +00:00
this . $textarea . change ( _ . bind ( function ( ) {
2012-08-31 10:50:42 +00:00
self . set ( { 'value' : instance . web . parse _value ( self . $textarea . val ( ) , self ) } ) ;
2012-04-17 15:45:05 +00:00
} , this ) ) ;
2012-04-18 16:10:47 +00:00
} else {
this . $textarea . attr ( 'disabled' , 'disabled' ) ;
2012-03-19 15:18:23 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . keyup ( function ( e ) {
2012-07-11 14:32:13 +00:00
if ( e . which === $ . ui . keyCode . ENTER ) {
e . stopPropagation ( ) ;
}
} ) ;
2012-06-25 13:52:15 +00:00
this . setupFocus ( this . $textarea ) ;
2011-04-05 09:55:49 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2012-08-31 10:50:42 +00:00
this . _super ( value _ ) ;
2012-03-19 15:18:23 +00:00
this . render _value ( ) ;
2012-08-31 10:50:42 +00:00
$ ( window ) . resize ( ) ;
2011-04-05 09:55:49 +00:00
} ,
2012-03-19 15:18:23 +00:00
render _value : function ( ) {
2012-04-17 14:32:27 +00:00
var show _value = instance . web . format _value ( this . get ( 'value' ) , this , '' ) ;
2012-08-31 10:50:42 +00:00
if ( show _value === '' ) {
this . $textarea . css ( 'height' , parseInt ( this . default _height ) + "px" ) ;
}
2012-04-18 16:10:47 +00:00
this . $textarea . val ( show _value ) ;
2012-08-31 10:50:42 +00:00
this . $textarea . autosize ( ) ;
2011-04-05 09:55:49 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2012-03-19 15:18:23 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
try {
2012-04-17 14:32:27 +00:00
var value _ = instance . web . parse _value ( this . $textarea . val ( ) , this , '' ) ;
2012-04-18 09:50:43 +00:00
return true ;
2012-03-19 15:18:23 +00:00
} catch ( e ) {
2012-04-18 09:16:13 +00:00
return false ;
2012-03-19 15:18:23 +00:00
}
2011-04-11 11:35:16 +00:00
}
2012-04-18 09:16:13 +00:00
return true ;
2011-06-20 18:25:49 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
2012-06-07 14:31:04 +00:00
return this . get ( 'value' ) === '' || this . _super ( ) ;
2011-06-20 18:25:49 +00:00
} ,
2012-08-24 18:27:07 +00:00
focus : function ( $el ) {
2012-08-01 16:49:33 +00:00
this . $textarea . focus ( ) ;
2011-12-21 17:16:02 +00:00
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
2012-06-15 14:35:20 +00:00
/ * *
* FieldTextHtml Widget
* Intended for FieldText widgets meant to display HTML content . This
* widget will instantiate the CLEditor ( see cleditor in static / src / lib )
* To find more information about CLEditor configutation : go to
* http : //premiumsoftware.net/cleditor/docs/GettingStarted.html
* /
2012-08-13 15:22:27 +00:00
instance . web . form . FieldTextHtml = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
template : 'FieldTextHtml' ,
2012-07-06 15:51:13 +00:00
initialize _content : function ( ) {
2012-08-13 15:40:11 +00:00
var self = this ;
if ( ! this . get ( "effective_readonly" ) ) {
self . _updating _editor = false ;
2012-08-24 18:27:07 +00:00
this . $textarea = this . $el . find ( 'textarea' ) ;
2012-08-13 15:40:11 +00:00
var width = ( ( this . node . attrs || { } ) . editor _width || 468 ) ;
2012-08-21 10:16:32 +00:00
var height = ( ( this . node . attrs || { } ) . editor _height || 250 ) ;
2012-08-13 15:40:11 +00:00
this . $textarea . cleditor ( {
width : width , // width not including margins, borders or padding
height : height , // height not including margins, borders or padding
controls : // controls to add to the toolbar
"bold italic underline strikethrough " +
"| removeformat | bullets numbering | outdent " +
"indent | link unlink | source" ,
bodyStyle : // style to assign to document body contained within the editor
"margin:4px; font:12px monospace; cursor:text; color:#1F1F1F"
} ) ;
this . $cleditor = this . $textarea . cleditor ( ) [ 0 ] ;
this . $cleditor . change ( function ( ) {
if ( ! self . _updating _editor ) {
self . $cleditor . updateTextArea ( ) ;
self . set ( { 'value' : self . $textarea . val ( ) } ) ;
}
} ) ;
}
2012-07-06 15:51:13 +00:00
} ,
set _value : function ( value _ ) {
this . _super . apply ( this , arguments ) ;
2012-08-13 15:22:27 +00:00
this . render _value ( ) ;
2012-06-15 14:35:20 +00:00
} ,
2012-07-06 15:51:13 +00:00
render _value : function ( ) {
2012-08-13 15:40:11 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
this . $textarea . val ( this . get ( 'value' ) ) ;
this . _updating _editor = true ;
this . $cleditor . updateFrame ( ) ;
this . _updating _editor = false ;
} else {
2012-08-24 18:27:07 +00:00
this . $el . html ( this . get ( 'value' ) ) ;
2012-08-13 15:40:11 +00:00
}
2012-07-06 07:44:35 +00:00
} ,
2012-06-15 14:35:20 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldBoolean = instance . web . form . AbstractField . extend ( {
2012-03-13 20:28:23 +00:00
template : 'FieldBoolean' ,
2011-04-05 09:55:49 +00:00
start : function ( ) {
2012-06-27 10:01:51 +00:00
var self = this ;
2011-04-05 09:55:49 +00:00
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $checkbox = $ ( "input" , this . $el ) ;
2012-06-25 13:52:15 +00:00
this . setupFocus ( this . $checkbox ) ;
2012-08-24 18:27:07 +00:00
this . $el . click ( _ . bind ( function ( ) {
2012-04-17 15:45:05 +00:00
this . set ( { 'value' : this . $checkbox . is ( ':checked' ) } ) ;
} , this ) ) ;
2012-03-16 11:52:43 +00:00
var check _readonly = function ( ) {
2012-06-27 10:01:51 +00:00
self . $checkbox . prop ( 'disabled' , self . get ( "effective_readonly" ) ) ;
2012-03-16 11:52:43 +00:00
} ;
this . on ( "change:effective_readonly" , this , check _readonly ) ;
2012-06-27 10:01:51 +00:00
check _readonly . call ( this ) ;
2011-04-11 11:35:16 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2011-04-05 14:34:25 +00:00
this . _super . apply ( this , arguments ) ;
2012-04-17 14:32:27 +00:00
this . $checkbox [ 0 ] . checked = value _ ;
2011-04-05 14:34:25 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-01 16:49:33 +00:00
this . $checkbox . focus ( ) ;
2011-03-30 14:00:48 +00:00
}
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldProgressBar = instance . web . form . AbstractField . extend ( {
2012-03-15 16:21:07 +00:00
template : 'FieldProgressBar' ,
2011-04-12 09:26:31 +00:00
start : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $el . progressbar ( {
2012-04-17 14:32:27 +00:00
value : this . get ( 'value' ) ,
2012-03-20 15:51:11 +00:00
disabled : this . get ( "effective_readonly" )
2011-04-12 09:26:31 +00:00
} ) ;
2011-05-09 14:10:01 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2011-05-09 14:10:01 +00:00
this . _super . apply ( this , arguments ) ;
2012-04-17 14:32:27 +00:00
var show _value = Number ( value _ ) ;
2011-07-04 12:10:57 +00:00
if ( isNaN ( show _value ) ) {
2011-05-09 14:10:01 +00:00
show _value = 0 ;
}
2012-04-17 12:09:49 +00:00
var formatted _value = instance . web . format _value ( show _value , { type : 'float' } , '0' ) ;
2012-08-24 18:27:07 +00:00
this . $el . progressbar ( 'option' , 'value' , show _value ) . find ( 'span' ) . html ( formatted _value + '%' ) ;
2011-04-12 09:26:31 +00:00
}
} ) ;
2011-03-30 14:00:48 +00:00
2012-06-21 23:56:41 +00:00
instance . web . form . FieldSelection = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-03-13 17:46:19 +00:00
template : 'FieldSelection' ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
2011-09-07 16:33:15 +00:00
var self = this ;
2012-04-18 12:45:20 +00:00
this . _super ( field _manager , node ) ;
2011-11-24 13:46:27 +00:00
this . values = _ . clone ( this . field . selection ) ;
2011-09-07 16:33:15 +00:00
_ . each ( this . values , function ( v , i ) {
if ( v [ 0 ] === false && v [ 1 ] === '' ) {
self . values . splice ( i , 1 ) ;
}
2011-07-07 14:19:24 +00:00
} ) ;
2011-09-07 16:33:15 +00:00
this . values . unshift ( [ false , '' ] ) ;
2011-03-30 14:00:48 +00:00
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2011-07-08 12:38:53 +00:00
// Flag indicating whether we're in an event chain containing a change
// event on the select, in order to know what to do on keyup[RETURN]:
// * If the user presses [RETURN] as part of changing the value of a
// selection, we should just let the value change and not let the
// event broadcast further (e.g. to validating the current state of
// the form in editable list view, which would lead to saving the
// current row or switching to the next one)
// * If the user presses [RETURN] with a select closed (side-effect:
// also if the user opened the select and pressed [RETURN] without
// changing the selected value), takes the action as validating the
// row
var ischanging = false ;
2012-08-24 18:27:07 +00:00
var $select = this . $el . find ( 'select' )
2012-04-17 15:45:05 +00:00
. change ( _ . bind ( function ( ) {
2012-08-24 18:27:07 +00:00
this . set ( { 'value' : this . values [ this . $el . find ( 'select' ) [ 0 ] . selectedIndex ] [ 0 ] } ) ;
2012-04-17 15:45:05 +00:00
} , this ) )
2011-07-08 12:38:53 +00:00
. change ( function ( ) { ischanging = true ; } )
. click ( function ( ) { ischanging = false ; } )
. keyup ( function ( e ) {
if ( e . which !== 13 || ! ischanging ) { return ; }
e . stopPropagation ( ) ;
ischanging = false ;
} ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
this . setupFocus ( $select ) ;
2011-04-05 14:34:25 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
value _ = value _ === null ? false : value _ ;
value _ = value _ instanceof Array ? value _ [ 0 ] : value _ ;
this . _super ( value _ ) ;
2012-03-16 12:23:31 +00:00
this . render _value ( ) ;
2011-04-11 11:35:16 +00:00
} ,
2012-03-16 12:23:31 +00:00
render _value : function ( ) {
if ( ! this . get ( "effective_readonly" ) ) {
var index = 0 ;
for ( var i = 0 , ii = this . values . length ; i < ii ; i ++ ) {
2012-04-17 14:32:27 +00:00
if ( this . values [ i ] [ 0 ] === this . get ( 'value' ) ) index = i ;
2012-03-16 12:23:31 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . find ( 'select' ) [ 0 ] . selectedIndex = index ;
2012-03-16 12:23:31 +00:00
} else {
var self = this ;
var option = _ ( this . values )
2012-07-12 12:56:37 +00:00
. detect ( function ( record ) { return record [ 0 ] === self . get ( 'value' ) ; } ) ;
2012-08-24 18:27:07 +00:00
this . $el . text ( option ? option [ 1 ] : this . values [ 0 ] [ 1 ] ) ;
2011-09-07 16:33:15 +00:00
}
2011-04-05 14:34:25 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2012-03-16 12:23:31 +00:00
if ( this . get ( "effective_readonly" ) ) {
2012-04-18 09:16:13 +00:00
return true ;
2012-03-16 12:23:31 +00:00
}
2012-08-24 18:27:07 +00:00
var value _ = this . values [ this . $el . find ( 'select' ) [ 0 ] . selectedIndex ] ;
2012-04-18 09:50:43 +00:00
return ! ! value _ ;
2011-06-20 18:25:49 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'select:first' ) . focus ( ) ;
2011-03-30 14:00:48 +00:00
}
2012-06-21 23:56:41 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
2012-08-29 15:37:56 +00:00
// jquery autocomplete tweak to allow html and classnames
2011-06-20 14:43:48 +00:00
( function ( ) {
2011-06-10 13:22:37 +00:00
var proto = $ . ui . autocomplete . prototype ,
initSource = proto . _initSource ;
2011-06-20 14:43:48 +00:00
2011-06-10 13:22:37 +00:00
function filter ( array , term ) {
var matcher = new RegExp ( $ . ui . autocomplete . escapeRegex ( term ) , "i" ) ;
2012-04-17 14:32:27 +00:00
return $ . grep ( array , function ( value _ ) {
return matcher . test ( $ ( "<div>" ) . html ( value _ . label || value _ . value || value _ ) . text ( ) ) ;
2011-06-10 13:22:37 +00:00
} ) ;
2011-06-10 10:31:55 +00:00
}
2011-06-20 14:43:48 +00:00
2011-06-10 13:22:37 +00:00
$ . extend ( proto , {
_initSource : function ( ) {
if ( this . options . html && $ . isArray ( this . options . source ) ) {
this . source = function ( request , response ) {
response ( filter ( this . options . source , request . term ) ) ;
} ;
} else {
initSource . call ( this ) ;
}
} ,
2011-06-20 14:43:48 +00:00
2011-06-10 13:22:37 +00:00
_renderItem : function ( ul , item ) {
return $ ( "<li></li>" )
. data ( "item.autocomplete" , item )
. append ( $ ( "<a></a>" ) [ this . options . html ? "html" : "text" ] ( item . label ) )
2012-08-29 15:37:56 +00:00
. appendTo ( ul )
. addClass ( item . classname ) ;
2011-06-10 13:22:37 +00:00
}
} ) ;
2011-06-20 14:43:48 +00:00
} ) ( ) ;
2011-06-10 10:31:55 +00:00
2012-05-07 12:22:46 +00:00
/ * *
* A mixin containing some useful methods to handle completion inputs .
* /
instance . web . form . CompletionFieldMixin = {
init : function ( ) {
this . limit = 7 ;
this . orderer = new instance . web . DropMisordered ( ) ;
} ,
/ * *
* Call this method to search using a string .
* /
2012-05-07 12:37:28 +00:00
get _search _result : function ( search _val ) {
2012-05-07 12:22:46 +00:00
var self = this ;
var dataset = new instance . web . DataSet ( this , this . field . relation , self . build _context ( ) ) ;
2012-05-30 09:39:16 +00:00
var blacklist = this . get _search _blacklist ( ) ;
2012-05-07 12:22:46 +00:00
2012-05-07 12:37:28 +00:00
return this . orderer . add ( dataset . name _search (
2012-05-30 09:39:16 +00:00
search _val , new instance . web . CompoundDomain ( self . build _domain ( ) , [ [ "id" , "not in" , blacklist ] ] ) ,
2012-06-28 12:52:42 +00:00
'ilike' , this . limit + 1 , self . build _context ( ) ) ) . pipe ( function ( data ) {
2012-05-07 12:22:46 +00:00
self . last _search = data ;
// possible selections for the m2o
var values = _ . map ( data , function ( x ) {
2012-06-28 13:08:08 +00:00
x [ 1 ] = x [ 1 ] . split ( "\n" ) [ 0 ] ;
2012-05-07 12:22:46 +00:00
return {
label : _ . str . escapeHTML ( x [ 1 ] ) ,
2012-06-28 13:08:08 +00:00
value : x [ 1 ] ,
name : x [ 1 ] ,
id : x [ 0 ] ,
2012-05-07 12:22:46 +00:00
} ;
} ) ;
// search more... if more results that max
if ( values . length > self . limit ) {
values = values . slice ( 0 , self . limit ) ;
2012-08-29 15:37:56 +00:00
values . push ( {
label : _t ( "Search More..." ) ,
action : function ( ) {
dataset . name _search ( search _val , self . build _domain ( ) , 'ilike' , false , function ( data ) {
self . _search _create _popup ( "search" , data ) ;
} ) ;
} ,
classname : 'oe_m2o_dropdown_option'
} ) ;
2012-05-07 12:22:46 +00:00
}
// quick create
var raw _result = _ ( data . result ) . map ( function ( x ) { return x [ 1 ] ; } ) ;
2012-05-07 12:37:28 +00:00
if ( search _val . length > 0 && ! _ . include ( raw _result , search _val ) ) {
2012-08-29 15:37:56 +00:00
values . push ( {
label : _ . str . sprintf ( _t ( 'Create "<strong>%s</strong>"' ) ,
$ ( '<span />' ) . text ( search _val ) . html ( ) ) ,
action : function ( ) {
self . _quick _create ( search _val ) ;
} ,
classname : 'oe_m2o_dropdown_option'
} ) ;
2012-05-07 12:22:46 +00:00
}
// create...
2012-08-29 15:37:56 +00:00
values . push ( {
label : _t ( "Create and Edit..." ) ,
action : function ( ) {
self . _search _create _popup ( "form" , undefined , self . _create _context ( search _val ) ) ;
} ,
classname : 'oe_m2o_dropdown_option'
} ) ;
2012-05-07 12:22:46 +00:00
2012-05-07 12:37:28 +00:00
return values ;
2012-05-07 12:22:46 +00:00
} ) ;
} ,
2012-05-30 09:39:16 +00:00
get _search _blacklist : function ( ) {
return [ ] ;
} ,
2012-05-07 12:22:46 +00:00
_quick _create : function ( name ) {
var self = this ;
var slow _create = function ( ) {
2012-08-24 17:26:22 +00:00
self . _search _create _popup ( "form" , undefined , self . _create _context ( name ) ) ;
2012-05-07 12:22:46 +00:00
} ;
2012-08-18 20:16:32 +00:00
if ( self . options . quick _create === undefined || self . options . quick _create ) {
2012-05-07 12:22:46 +00:00
new instance . web . DataSet ( this , this . field . relation , self . build _context ( ) )
. name _create ( name , function ( data ) {
2012-05-09 15:32:46 +00:00
self . add _id ( data [ 0 ] ) ;
2012-05-07 12:22:46 +00:00
} ) . fail ( function ( error , event ) {
event . preventDefault ( ) ;
slow _create ( ) ;
} ) ;
} else
slow _create ( ) ;
} ,
// all search/create popup handling
_search _create _popup : function ( view , ids , context ) {
var self = this ;
var pop = new instance . web . form . SelectCreatePopup ( this ) ;
pop . select _element (
self . field . relation ,
{
2012-06-29 15:02:25 +00:00
title : ( view === 'search' ? _t ( "Search: " ) : _t ( "Create: " ) ) + this . string ,
2012-05-07 12:22:46 +00:00
initial _ids : ids ? _ . map ( ids , function ( x ) { return x [ 0 ] } ) : undefined ,
initial _view : view ,
disable _multiple _selection : true
} ,
self . build _domain ( ) ,
new instance . web . CompoundContext ( self . build _context ( ) , context || { } )
) ;
pop . on _select _elements . add ( function ( element _ids ) {
2012-05-09 15:32:46 +00:00
self . add _id ( element _ids [ 0 ] ) ;
2012-06-25 13:52:15 +00:00
self . focus ( ) ;
2012-05-07 12:22:46 +00:00
} ) ;
} ,
2012-05-09 15:32:46 +00:00
/ * *
* To implement .
* /
add _id : function ( id ) { } ,
2012-08-24 17:26:22 +00:00
_create _context : function ( name ) {
var tmp = { } ;
var field = ( this . options || { } ) . create _name _field ;
2012-09-05 09:42:19 +00:00
if ( field !== false && ( this . options || { } ) . quick _create !== false )
tmp [ "default_" + field ] = name || "name" ;
2012-08-24 17:26:22 +00:00
return tmp ;
} ,
2012-05-07 12:22:46 +00:00
} ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldMany2One = instance . web . form . AbstractField . extend ( instance . web . form . CompletionFieldMixin , instance . web . form . ReinitializeFieldMixin , {
2012-03-20 10:26:50 +00:00
template : "FieldMany2One" ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2012-05-07 12:22:46 +00:00
instance . web . form . CompletionFieldMixin . init . call ( this ) ;
2012-04-17 14:32:27 +00:00
this . set ( { 'value' : false } ) ;
2012-04-05 14:41:18 +00:00
this . display _value = { } ;
2011-06-10 13:22:37 +00:00
this . last _search = [ ] ;
2012-04-05 14:41:18 +00:00
this . floating = false ;
2012-04-05 14:53:22 +00:00
this . inhibit _on _change = false ;
2012-07-09 11:57:27 +00:00
this . current _display = null ;
2011-04-27 12:20:51 +00:00
} ,
start : function ( ) {
2011-06-06 16:50:43 +00:00
this . _super ( ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . ReinitializeFieldMixin . start . call ( this ) ;
2012-04-05 14:41:18 +00:00
this . on ( "change:value" , this , function ( ) {
this . floating = false ;
this . render _value ( ) ;
} ) ;
2012-08-16 12:55:40 +00:00
instance . web . bus . on ( 'click' , this , function ( ) {
if ( ! this . get ( "effective_readonly" ) && this . $input && this . $input . autocomplete ( 'widget' ) . is ( ':visible' ) ) {
this . $input . autocomplete ( "close" ) ;
}
} ) ;
2012-03-16 09:54:59 +00:00
} ,
2012-03-20 10:26:50 +00:00
initialize _content : function ( ) {
2012-03-15 16:43:26 +00:00
if ( ! this . get ( "effective_readonly" ) )
2012-03-13 15:14:35 +00:00
this . render _editable ( ) ;
this . render _value ( ) ;
} ,
render _editable : function ( ) {
2011-06-06 16:50:43 +00:00
var self = this ;
2012-08-24 18:27:07 +00:00
this . $input = this . $el . find ( "input" ) ;
2012-07-12 12:56:37 +00:00
2012-04-05 12:44:33 +00:00
self . $input . tipsy ( {
title : function ( ) {
2012-08-06 14:00:44 +00:00
return QWeb . render ( 'Tipsy.alert' , {
message : "No element was selected, you should create or select one from the dropdown list."
} ) ;
2012-04-05 12:44:33 +00:00
} ,
trigger : 'manual' ,
fade : true ,
2012-08-06 14:00:44 +00:00
gravity : 's' ,
html : true ,
opacity : 1 ,
offset : 4 ,
} ) ;
self . $input . on ( 'focus' , function ( ) {
self . $input . tipsy ( "hide" ) ;
2012-04-05 12:44:33 +00:00
} ) ;
2012-07-12 12:56:37 +00:00
2012-08-24 18:27:07 +00:00
this . $drop _down = this . $el . find ( ".oe_m2o_drop_down_button" ) ;
this . $follow _button = $ ( ".oe_m2o_cm_button" , this . $el ) ;
2012-07-12 12:56:37 +00:00
2012-04-05 12:07:18 +00:00
this . $follow _button . click ( function ( ) {
2012-04-05 14:41:18 +00:00
if ( ! self . get ( 'value' ) ) {
2012-06-25 13:52:15 +00:00
self . focus ( ) ;
2012-04-05 12:07:18 +00:00
return ;
}
2012-04-17 12:09:49 +00:00
var pop = new instance . web . form . FormOpenPopup ( self . view ) ;
2012-04-05 12:07:18 +00:00
pop . show _element (
self . field . relation ,
2012-04-05 14:41:18 +00:00
self . get ( "value" ) ,
2012-04-05 12:07:18 +00:00
self . build _context ( ) ,
{
2012-06-29 15:02:25 +00:00
title : _t ( "Open: " ) + self . string
2012-04-05 12:07:18 +00:00
}
) ;
pop . on _write _completed . add _last ( function ( ) {
2012-04-05 14:41:18 +00:00
self . display _value = { } ;
self . render _value ( ) ;
2012-06-25 13:52:15 +00:00
self . focus ( ) ;
2011-08-25 14:21:26 +00:00
} ) ;
2011-06-10 13:22:37 +00:00
} ) ;
2011-06-20 14:43:48 +00:00
2011-06-10 10:31:55 +00:00
// some behavior for input
2012-08-08 14:13:01 +00:00
var input _changed = function ( ) {
2012-07-09 11:57:27 +00:00
if ( self . current _display !== self . $input . val ( ) ) {
self . current _display = self . $input . val ( ) ;
if ( self . $input . val ( ) === "" ) {
self . set ( { value : false } ) ;
2012-07-10 08:54:41 +00:00
self . floating = false ;
2012-07-09 11:57:27 +00:00
} else {
self . floating = true ;
}
2011-06-08 15:29:44 +00:00
}
2012-08-08 14:13:01 +00:00
} ;
this . $input . keydown ( input _changed ) ;
this . $input . change ( input _changed ) ;
2011-06-09 17:36:44 +00:00
this . $drop _down . click ( function ( ) {
2011-06-08 15:47:44 +00:00
if ( self . $input . autocomplete ( "widget" ) . is ( ":visible" ) ) {
2011-06-08 15:29:44 +00:00
self . $input . autocomplete ( "close" ) ;
2012-06-20 11:15:27 +00:00
self . $input . focus ( ) ;
2011-06-08 15:29:44 +00:00
} else {
2012-04-05 14:41:18 +00:00
if ( self . get ( "value" ) && ! self . floating ) {
2011-06-10 13:55:26 +00:00
self . $input . autocomplete ( "search" , "" ) ;
} else {
self . $input . autocomplete ( "search" ) ;
}
2011-06-08 15:29:44 +00:00
}
2011-06-06 16:50:43 +00:00
} ) ;
2012-08-10 13:48:15 +00:00
self . tip _def = $ . Deferred ( ) ;
self . untip _def = $ . Deferred ( ) ;
2012-04-05 12:44:33 +00:00
var tip _delay = 200 ;
2012-08-06 14:00:44 +00:00
var tip _duration = 15000 ;
2011-06-10 13:22:37 +00:00
var anyoneLoosesFocus = function ( ) {
2012-06-14 14:23:09 +00:00
var used = false ;
2012-04-05 14:41:18 +00:00
if ( self . floating ) {
if ( self . last _search . length > 0 ) {
if ( self . last _search [ 0 ] [ 0 ] != self . get ( "value" ) ) {
self . display _value = { } ;
self . display _value [ "" + self . last _search [ 0 ] [ 0 ] ] = self . last _search [ 0 ] [ 1 ] ;
self . set ( { value : self . last _search [ 0 ] [ 0 ] } ) ;
} else {
2012-06-14 14:23:09 +00:00
used = true ;
2012-04-05 14:41:18 +00:00
self . render _value ( ) ;
}
2011-06-10 13:22:37 +00:00
} else {
2012-06-14 14:23:09 +00:00
used = true ;
2012-04-05 14:41:18 +00:00
self . set ( { value : false } ) ;
2012-06-14 14:23:09 +00:00
self . render _value ( ) ;
2011-06-10 13:22:37 +00:00
}
2012-06-14 14:23:09 +00:00
self . floating = false ;
2011-06-10 13:22:37 +00:00
}
2012-08-10 13:48:15 +00:00
if ( used && self . get ( "value" ) === false && ! self . no _tipsy ) {
self . tip _def . reject ( ) ;
self . untip _def . reject ( ) ;
self . tip _def = $ . Deferred ( ) ;
self . tip _def . then ( function ( ) {
2012-04-05 12:44:33 +00:00
self . $input . tipsy ( "show" ) ;
} ) ;
setTimeout ( function ( ) {
2012-08-10 13:48:15 +00:00
self . tip _def . resolve ( ) ;
self . untip _def . reject ( ) ;
self . untip _def = $ . Deferred ( ) ;
self . untip _def . then ( function ( ) {
2012-04-05 12:44:33 +00:00
self . $input . tipsy ( "hide" ) ;
} ) ;
2012-08-10 13:48:15 +00:00
setTimeout ( function ( ) { self . untip _def . resolve ( ) ; } , tip _duration ) ;
2012-04-05 12:44:33 +00:00
} , tip _delay ) ;
} else {
2012-08-10 13:48:15 +00:00
self . no _tipsy = false ;
self . tip _def . reject ( ) ;
2012-04-05 12:44:33 +00:00
}
2011-09-06 08:58:53 +00:00
} ;
2012-09-05 12:04:55 +00:00
var ignore _blur = false ;
this . $input . on ( {
focusout : anyoneLoosesFocus ,
focus : function ( ) { self . trigger ( 'focused' ) ; } ,
autocompleteopen : function ( ) { ignore _blur = true ; } ,
autocompleteclose : function ( ) { ignore _blur = false ; } ,
blur : function ( ) {
// autocomplete open
if ( ignore _blur ) { return ; }
if ( _ ( self . getChildren ( ) ) . any ( function ( child ) {
return child instanceof instance . web . form . AbstractFormPopup ;
} ) ) { return ; }
self . trigger ( 'blurred' ) ;
}
} ) ;
2011-06-20 14:43:48 +00:00
2011-07-12 14:16:10 +00:00
var isSelecting = false ;
2011-06-10 10:31:55 +00:00
// autocomplete
2011-06-06 16:50:43 +00:00
this . $input . autocomplete ( {
2012-05-07 12:37:28 +00:00
source : function ( req , resp ) {
self . get _search _result ( req . term ) . then ( function ( result ) {
resp ( result ) ;
} ) ;
} ,
2011-06-09 13:20:04 +00:00
select : function ( event , ui ) {
2011-07-12 14:16:10 +00:00
isSelecting = true ;
2011-06-08 15:29:44 +00:00
var item = ui . item ;
if ( item . id ) {
2012-04-05 14:41:18 +00:00
self . display _value = { } ;
self . display _value [ "" + item . id ] = item . name ;
self . set ( { value : item . id } ) ;
2011-06-08 15:29:44 +00:00
} else if ( item . action ) {
item . action ( ) ;
2012-08-02 12:02:30 +00:00
// Cancel widget blurring, to avoid form blur event
self . trigger ( 'focused' ) ;
2011-06-08 15:29:44 +00:00
return false ;
}
2011-06-10 10:31:55 +00:00
} ,
2011-06-10 12:15:45 +00:00
focus : function ( e , ui ) {
e . preventDefault ( ) ;
} ,
2011-06-10 13:22:37 +00:00
html : true ,
2012-06-14 14:23:09 +00:00
// disabled to solve a bug, but may cause others
//close: anyoneLoosesFocus,
2011-06-10 13:58:35 +00:00
minLength : 0 ,
delay : 0
2011-06-06 16:50:43 +00:00
} ) ;
2012-02-17 13:45:23 +00:00
this . $input . autocomplete ( "widget" ) . addClass ( "openerp" ) ;
2011-07-12 14:16:10 +00:00
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this . $input . keyup ( function ( e ) {
2012-07-09 11:57:27 +00:00
if ( e . which === 13 ) { // ENTER
2011-07-12 14:16:10 +00:00
if ( isSelecting )
e . stopPropagation ( ) ;
}
isSelecting = false ;
} ) ;
2012-09-05 12:04:55 +00:00
this . setupFocus ( this . $follow _button ) ;
2011-06-06 16:50:43 +00:00
} ,
2012-04-05 14:41:18 +00:00
render _value : function ( no _recurse ) {
var self = this ;
if ( ! this . get ( "value" ) ) {
this . display _string ( "" ) ;
return ;
2011-06-28 16:22:06 +00:00
}
2012-04-05 14:41:18 +00:00
var display = this . display _value [ "" + this . get ( "value" ) ] ;
if ( display ) {
this . display _string ( display ) ;
2011-06-28 16:22:06 +00:00
return ;
}
2012-04-05 14:41:18 +00:00
if ( ! no _recurse ) {
2012-06-28 12:52:42 +00:00
var dataset = new instance . web . DataSetStatic ( this , this . field . relation , self . build _context ( ) ) ;
2012-04-05 14:41:18 +00:00
dataset . name _get ( [ self . get ( "value" ) ] , function ( data ) {
self . display _value [ "" + self . get ( "value" ) ] = data [ 0 ] [ 1 ] ;
self . render _value ( true ) ;
} ) ;
2011-04-05 14:34:25 +00:00
}
2011-05-03 08:50:44 +00:00
} ,
2012-04-05 14:41:18 +00:00
display _string : function ( str ) {
2011-06-23 12:40:16 +00:00
var self = this ;
2012-03-15 16:43:26 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
2012-06-28 12:36:21 +00:00
this . $input . val ( str . split ( "\n" ) [ 0 ] ) ;
2012-07-09 11:57:27 +00:00
this . current _display = this . $input . val ( ) ;
2012-08-07 10:21:23 +00:00
this . $ ( '.oe_m2o_cm_button' ) . css ( { 'visibility' : this . is _false ( ) ? 'hidden' : 'visible' } ) ;
2011-06-23 12:40:16 +00:00
} else {
2012-07-18 10:17:29 +00:00
var lines = _ . escape ( str ) . split ( "\n" ) ;
var link = "" ;
var follow = "" ;
2012-08-18 20:16:32 +00:00
if ( ! this . options . highlight _first _line ) {
2012-07-18 10:17:29 +00:00
link = lines . join ( "<br />" ) ;
} else {
link = lines [ 0 ] ;
follow = _ . rest ( lines ) . join ( "<br />" ) ;
if ( follow )
link += "<br />" ;
}
2012-08-24 18:27:07 +00:00
var $link = this . $el . find ( '.oe_form_uri' )
2012-03-13 15:14:35 +00:00
. unbind ( 'click' )
2012-07-25 12:35:43 +00:00
. html ( link ) ;
2012-08-18 20:16:32 +00:00
if ( ! this . options . no _open )
2012-07-25 12:35:43 +00:00
$link . click ( function ( ) {
2012-03-13 15:14:35 +00:00
self . do _action ( {
type : 'ir.actions.act_window' ,
res _model : self . field . relation ,
2012-04-05 14:41:18 +00:00
res _id : self . get ( "value" ) ,
2012-03-13 15:14:35 +00:00
context : self . build _context ( ) ,
2012-04-09 21:30:43 +00:00
views : [ [ false , 'form' ] ] ,
2012-03-13 15:14:35 +00:00
target : 'current'
} ) ;
return false ;
} ) ;
2012-08-24 18:27:07 +00:00
$ ( ".oe_form_m2o_follow" , this . $el ) . html ( follow ) ;
2011-06-10 14:58:01 +00:00
}
2011-08-25 17:04:10 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2011-08-25 17:04:10 +00:00
var self = this ;
2012-04-17 14:32:27 +00:00
if ( value _ instanceof Array ) {
2012-04-05 14:41:18 +00:00
this . display _value = { } ;
2012-08-18 20:16:32 +00:00
if ( ! this . options . always _reload ) {
2012-06-28 12:52:42 +00:00
this . display _value [ "" + value _ [ 0 ] ] = value _ [ 1 ] ;
}
2012-04-17 14:32:27 +00:00
value _ = value _ [ 0 ] ;
2011-06-23 12:40:16 +00:00
}
2012-04-17 14:32:27 +00:00
value _ = value _ || false ;
2012-04-05 14:53:22 +00:00
this . inhibit _on _change = true ;
2012-04-17 14:32:27 +00:00
this . _super ( value _ ) ;
2012-04-05 14:53:22 +00:00
this . inhibit _on _change = false ;
2011-10-06 12:04:25 +00:00
} ,
2012-07-19 08:19:14 +00:00
get _displayed : function ( ) {
return this . display _value [ "" + this . get ( "value" ) ] ;
} ,
2012-05-09 15:32:46 +00:00
add _id : function ( id ) {
this . display _value = { } ;
this . set ( { value : id } ) ;
} ,
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
return ! this . get ( "value" ) ;
2011-10-24 15:20:56 +00:00
} ,
2012-04-18 12:37:04 +00:00
focus : function ( ) {
2012-08-01 16:49:33 +00:00
this . $input . focus ( ) ;
2012-08-10 13:48:15 +00:00
} ,
_quick _create : function ( ) {
this . no _tipsy = true ;
2012-08-16 14:20:02 +00:00
this . tip _def . reject ( ) ;
2012-08-10 13:48:15 +00:00
return instance . web . form . CompletionFieldMixin . _quick _create . apply ( this , arguments ) ;
} ,
_search _create _popup : function ( ) {
this . no _tipsy = true ;
2012-08-16 14:20:02 +00:00
this . tip _def . reject ( ) ;
2012-08-10 13:48:15 +00:00
return instance . web . form . CompletionFieldMixin . _search _create _popup . apply ( this , arguments ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
2011-06-21 12:12:22 +00:00
/ *
# Values : ( 0 , 0 , { fields } ) create
# ( 1 , ID , { fields } ) update
# ( 2 , ID ) remove ( delete )
# ( 3 , ID ) unlink one ( target id or target of relation )
# ( 4 , ID ) link
# ( 5 ) unlink all ( only valid for one2many )
* /
2011-06-28 08:22:55 +00:00
var commands = {
// (0, _, {values})
CREATE : 0 ,
2011-06-28 08:30:50 +00:00
'create' : function ( values ) {
return [ commands . CREATE , false , values ] ;
} ,
2011-06-28 08:22:55 +00:00
// (1, id, {values})
UPDATE : 1 ,
2011-06-28 08:30:50 +00:00
'update' : function ( id , values ) {
return [ commands . UPDATE , id , values ] ;
} ,
2011-06-28 08:22:55 +00:00
// (2, id[, _])
DELETE : 2 ,
2011-06-28 08:30:50 +00:00
'delete' : function ( id ) {
return [ commands . DELETE , id , false ] ;
} ,
2011-06-28 08:22:55 +00:00
// (3, id[, _]) removes relation, but not linked record itself
FORGET : 3 ,
2011-06-28 08:30:50 +00:00
'forget' : function ( id ) {
return [ commands . FORGET , id , false ] ;
} ,
2011-06-28 08:22:55 +00:00
// (4, id[, _])
LINK _TO : 4 ,
2011-06-28 08:30:50 +00:00
'link_to' : function ( id ) {
return [ commands . LINK _TO , id , false ] ;
} ,
2011-06-28 08:22:55 +00:00
// (5[, _[, _]])
2011-06-29 15:35:26 +00:00
DELETE _ALL : 5 ,
'delete_all' : function ( ) {
2011-06-28 08:30:50 +00:00
return [ 5 , false , false ] ;
} ,
2011-06-28 08:22:55 +00:00
// (6, _, ids) replaces all linked records with provided ids
2011-06-28 08:30:50 +00:00
REPLACE _WITH : 6 ,
'replace_with' : function ( ids ) {
return [ 6 , false , ids ] ;
}
2011-06-28 08:22:55 +00:00
} ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldOne2Many = instance . web . form . AbstractField . extend ( {
2011-07-04 15:00:17 +00:00
multi _selection : false ,
2012-03-29 10:03:08 +00:00
disable _utility _classes : true ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2012-04-06 12:30:28 +00:00
lazy _build _o2m _kanban _view ( ) ;
2011-10-26 15:31:09 +00:00
this . is _loaded = $ . Deferred ( ) ;
this . initial _is _loaded = this . is _loaded ;
2011-10-17 16:22:28 +00:00
this . is _setted = $ . Deferred ( ) ;
2011-07-18 15:28:07 +00:00
this . form _last _update = $ . Deferred ( ) ;
2011-10-12 16:13:22 +00:00
this . init _form _last _update = this . form _last _update ;
2011-04-05 10:04:50 +00:00
} ,
start : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $el . addClass ( 'oe_form_field oe_form_field_one2many' ) ;
2011-06-20 14:43:48 +00:00
2011-05-31 14:11:19 +00:00
var self = this ;
2011-06-20 14:43:48 +00:00
2012-04-17 12:09:49 +00:00
this . dataset = new instance . web . form . One2ManyDataSet ( this , this . field . relation ) ;
2011-06-29 14:35:06 +00:00
this . dataset . o2m = this ;
this . dataset . parent _view = this . view ;
2011-12-19 17:30:55 +00:00
this . dataset . child _name = this . name ;
2011-06-21 12:12:22 +00:00
this . dataset . on _change . add _last ( function ( ) {
2012-01-03 17:17:13 +00:00
self . trigger _on _change ( ) ;
2011-05-31 14:11:19 +00:00
} ) ;
2011-06-20 14:43:48 +00:00
2011-10-26 12:55:24 +00:00
this . is _setted . then ( function ( ) {
self . load _views ( ) ;
} ) ;
2012-03-16 10:48:59 +00:00
this . is _loaded . then ( function ( ) {
self . on ( "change:effective_readonly" , self , function ( ) {
self . is _loaded = self . is _loaded . pipe ( function ( ) {
self . viewmanager . destroy ( ) ;
return $ . when ( self . load _views ( ) ) . then ( function ( ) {
self . reload _current _view ( ) ;
} ) ;
} ) ;
} ) ;
} ) ;
2011-10-26 12:55:24 +00:00
} ,
2012-01-03 17:17:13 +00:00
trigger _on _change : function ( ) {
var tmp = this . doing _on _change ;
this . doing _on _change = true ;
2012-04-18 12:23:41 +00:00
this . trigger ( 'changed_value' ) ;
2012-01-03 17:17:13 +00:00
this . doing _on _change = tmp ;
} ,
2011-10-26 12:55:24 +00:00
load _views : function ( ) {
var self = this ;
2012-07-12 12:56:37 +00:00
2011-06-06 07:04:51 +00:00
var modes = this . node . attrs . mode ;
2011-10-25 12:42:51 +00:00
modes = ! ! modes ? modes . split ( "," ) : [ "tree" ] ;
2011-06-06 07:04:51 +00:00
var views = [ ] ;
_ . each ( modes , function ( mode ) {
2012-06-11 16:07:29 +00:00
if ( ! _ . include ( [ "list" , "tree" , "graph" , "kanban" ] , mode ) ) {
2012-06-18 14:02:50 +00:00
try {
throw new Error ( _ . str . sprintf ( "View type '%s' is not supported in One2Many." , mode ) ) ;
} catch ( e ) {
instance . webclient . crashmanager . on _javascript _exception ( e )
}
2012-06-11 16:07:29 +00:00
}
2011-08-08 17:32:30 +00:00
var view = {
view _id : false ,
view _type : mode == "tree" ? "list" : mode ,
2012-04-18 14:24:39 +00:00
options : { }
2011-08-08 17:32:30 +00:00
} ;
2011-06-06 07:04:51 +00:00
if ( self . field . views && self . field . views [ mode ] ) {
2011-06-16 14:25:30 +00:00
view . embedded _view = self . field . views [ mode ] ;
}
if ( view . view _type === "list" ) {
2012-07-02 16:09:51 +00:00
_ . extend ( view . options , {
2012-07-26 15:55:10 +00:00
addable : null ,
2012-07-02 16:09:51 +00:00
selectable : self . multi _selection ,
sortable : false ,
import _enabled : false ,
2012-07-11 14:26:04 +00:00
deletable : true
2012-07-02 16:09:51 +00:00
} ) ;
2012-03-16 10:48:59 +00:00
if ( self . get ( "effective_readonly" ) ) {
2012-07-02 16:09:51 +00:00
_ . extend ( view . options , {
deletable : null ,
reorderable : false ,
} ) ;
2011-10-26 14:11:48 +00:00
}
2011-09-30 15:46:12 +00:00
} else if ( view . view _type === "form" ) {
2012-03-16 10:48:59 +00:00
if ( self . get ( "effective_readonly" ) ) {
2012-04-09 21:30:43 +00:00
view . view _type = 'form' ;
2011-12-07 09:42:42 +00:00
}
2012-07-02 16:09:51 +00:00
_ . extend ( view . options , {
not _interactible _on _create : true ,
} ) ;
2012-05-14 15:28:46 +00:00
} else if ( view . view _type === "kanban" ) {
2012-07-02 16:09:51 +00:00
_ . extend ( view . options , {
confirm _on _delete : false ,
} ) ;
2012-05-14 15:28:46 +00:00
if ( self . get ( "effective_readonly" ) ) {
2012-07-02 16:09:51 +00:00
_ . extend ( view . options , {
action _buttons : false ,
quick _creatable : false ,
creatable : false ,
read _only _mode : true ,
} ) ;
2012-05-14 15:28:46 +00:00
}
2011-06-06 07:04:51 +00:00
}
views . push ( view ) ;
} ) ;
2011-06-16 16:37:09 +00:00
this . views = views ;
2011-12-07 09:42:42 +00:00
2012-05-16 10:16:34 +00:00
this . viewmanager = new instance . web . form . One2ManyViewManager ( this , this . dataset , views , { } ) ;
this . viewmanager . o2m = self ;
2011-07-18 15:28:07 +00:00
var once = $ . Deferred ( ) . then ( function ( ) {
2011-10-12 16:13:22 +00:00
self . init _form _last _update . resolve ( ) ;
2011-06-28 08:22:28 +00:00
} ) ;
2011-10-26 12:55:24 +00:00
var def = $ . Deferred ( ) . then ( function ( ) {
2011-10-26 15:31:09 +00:00
self . initial _is _loaded . resolve ( ) ;
2011-10-26 12:55:24 +00:00
} ) ;
2011-06-01 08:23:58 +00:00
this . viewmanager . on _controller _inited . add _last ( function ( view _type , controller ) {
2012-04-06 12:30:28 +00:00
controller . o2m = self ;
2011-05-31 15:28:07 +00:00
if ( view _type == "list" ) {
2012-07-24 17:05:50 +00:00
if ( self . get ( "effective_readonly" ) ) {
controller . on ( 'edit:before' , self , function ( e ) {
e . cancel = true ;
} ) ;
}
2012-04-10 14:29:01 +00:00
} else if ( view _type === "form" ) {
if ( self . get ( "effective_readonly" ) ) {
2012-08-24 18:27:07 +00:00
$ ( ".oe_form_buttons" , controller . $el ) . children ( ) . remove ( ) ;
2011-12-23 11:12:11 +00:00
}
2011-07-18 15:28:07 +00:00
controller . on _record _loaded . add _last ( function ( ) {
once . resolve ( ) ;
} ) ;
2011-07-27 14:03:32 +00:00
controller . on _pager _action . add _first ( function ( ) {
2011-11-10 11:02:06 +00:00
self . save _any _view ( ) ;
2011-07-27 14:03:32 +00:00
} ) ;
2011-10-07 16:21:56 +00:00
} else if ( view _type == "graph" ) {
self . reload _current _view ( )
2011-05-31 13:01:54 +00:00
}
2011-10-26 12:55:24 +00:00
def . resolve ( ) ;
2011-05-31 13:01:54 +00:00
} ) ;
2011-10-26 15:39:44 +00:00
this . viewmanager . on _mode _switch . add _first ( function ( n _mode , b , c , d , e ) {
2011-11-10 11:02:06 +00:00
$ . when ( self . save _any _view ( ) ) . then ( function ( ) {
2011-10-26 15:39:44 +00:00
if ( n _mode === "list" )
2011-12-19 16:28:05 +00:00
$ . async _when ( ) . then ( function ( ) { self . reload _current _view ( ) ; } ) ;
2011-10-26 15:39:44 +00:00
} ) ;
2011-07-27 14:03:32 +00:00
} ) ;
2011-10-17 16:22:28 +00:00
this . is _setted . then ( function ( ) {
2011-12-19 16:28:05 +00:00
$ . async _when ( ) . then ( function ( ) {
2012-08-24 18:27:07 +00:00
self . viewmanager . appendTo ( self . $el ) ;
2011-12-19 16:28:05 +00:00
} ) ;
2011-10-17 16:22:28 +00:00
} ) ;
2011-10-26 12:55:24 +00:00
return def ;
2011-04-04 15:52:09 +00:00
} ,
2011-06-16 16:37:09 +00:00
reload _current _view : function ( ) {
var self = this ;
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
return self . is _loaded = self . is _loaded . pipe ( function ( ) {
2011-12-07 09:42:42 +00:00
var active _view = self . viewmanager . active _view ;
var view = self . viewmanager . views [ active _view ] . controller ;
if ( active _view === "list" ) {
2011-10-26 15:31:09 +00:00
return view . reload _content ( ) ;
2012-04-10 14:29:01 +00:00
} else if ( active _view === "form" ) {
2011-10-26 15:31:09 +00:00
if ( self . dataset . index === null && self . dataset . ids . length >= 1 ) {
self . dataset . index = 0 ;
}
var act = function ( ) {
return view . do _show ( ) ;
2011-12-07 09:42:42 +00:00
} ;
2011-10-26 15:31:09 +00:00
self . form _last _update = self . form _last _update . pipe ( act , act ) ;
return self . form _last _update ;
2012-04-06 12:03:47 +00:00
} else if ( view . do _search ) {
2011-10-26 15:31:09 +00:00
return view . do _search ( self . build _domain ( ) , self . dataset . get _context ( ) , [ ] ) ;
2011-10-12 12:49:23 +00:00
}
2011-12-07 09:42:42 +00:00
} , undefined ) ;
2011-06-16 16:37:09 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
value _ = value _ || [ ] ;
2011-06-23 10:42:22 +00:00
var self = this ;
2011-06-29 15:35:26 +00:00
this . dataset . reset _ids ( [ ] ) ;
2012-04-17 14:32:27 +00:00
if ( value _ . length >= 1 && value _ [ 0 ] instanceof Array ) {
2011-06-27 09:05:32 +00:00
var ids = [ ] ;
2012-04-17 14:32:27 +00:00
_ . each ( value _ , function ( command ) {
2011-06-28 08:22:55 +00:00
var obj = { values : command [ 2 ] } ;
switch ( command [ 0 ] ) {
case commands . CREATE :
obj [ 'id' ] = _ . uniqueId ( self . dataset . virtual _id _prefix ) ;
2011-09-28 11:55:37 +00:00
obj . defaults = { } ;
2011-06-28 08:22:55 +00:00
self . dataset . to _create . push ( obj ) ;
2012-02-14 16:15:58 +00:00
self . dataset . cache . push ( _ . extend ( _ . clone ( obj ) , { values : _ . clone ( command [ 2 ] ) } ) ) ;
2011-06-28 08:22:55 +00:00
ids . push ( obj . id ) ;
return ;
case commands . UPDATE :
obj [ 'id' ] = command [ 1 ] ;
self . dataset . to _write . push ( obj ) ;
2012-02-14 16:15:58 +00:00
self . dataset . cache . push ( _ . extend ( _ . clone ( obj ) , { values : _ . clone ( command [ 2 ] ) } ) ) ;
2011-06-28 08:22:55 +00:00
ids . push ( obj . id ) ;
return ;
case commands . DELETE :
self . dataset . to _delete . push ( { id : command [ 1 ] } ) ;
return ;
case commands . LINK _TO :
ids . push ( command [ 1 ] ) ;
return ;
2011-06-29 15:35:26 +00:00
case commands . DELETE _ALL :
self . dataset . delete _all = true ;
return ;
2011-06-27 09:05:32 +00:00
}
} ) ;
this . _super ( ids ) ;
this . dataset . set _ids ( ids ) ;
2012-04-17 14:32:27 +00:00
} else if ( value _ . length >= 1 && typeof ( value _ [ 0 ] ) === "object" ) {
2011-06-30 09:14:39 +00:00
var ids = [ ] ;
this . dataset . delete _all = true ;
2012-04-17 14:32:27 +00:00
_ . each ( value _ , function ( command ) {
2011-06-30 09:14:39 +00:00
var obj = { values : command } ;
obj [ 'id' ] = _ . uniqueId ( self . dataset . virtual _id _prefix ) ;
2011-09-28 11:55:37 +00:00
obj . defaults = { } ;
2011-06-30 09:14:39 +00:00
self . dataset . to _create . push ( obj ) ;
self . dataset . cache . push ( _ . clone ( obj ) ) ;
ids . push ( obj . id ) ;
} ) ;
this . _super ( ids ) ;
this . dataset . set _ids ( ids ) ;
2011-06-27 09:05:32 +00:00
} else {
2012-04-17 14:32:27 +00:00
this . _super ( value _ ) ;
this . dataset . reset _ids ( value _ ) ;
2011-06-27 09:05:32 +00:00
}
2011-07-27 09:09:05 +00:00
if ( this . dataset . index === null && this . dataset . ids . length > 0 ) {
this . dataset . index = 0 ;
}
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
self . is _setted . resolve ( ) ;
return self . reload _current _view ( ) ;
2011-05-31 13:38:17 +00:00
} ,
2011-06-21 12:12:22 +00:00
get _value : function ( ) {
2011-06-27 09:05:32 +00:00
var self = this ;
2011-06-29 14:35:06 +00:00
if ( ! this . dataset )
return [ ] ;
2011-12-05 15:38:13 +00:00
this . save _any _view ( ) ;
2011-06-29 15:35:26 +00:00
var val = this . dataset . delete _all ? [ commands . delete _all ( ) ] : [ ] ;
val = val . concat ( _ . map ( this . dataset . ids , function ( id ) {
2011-06-27 09:05:32 +00:00
var alter _order = _ . detect ( self . dataset . to _create , function ( x ) { return x . id === id ; } ) ;
if ( alter _order ) {
2011-06-28 08:30:50 +00:00
return commands . create ( alter _order . values ) ;
2011-06-27 09:05:32 +00:00
}
alter _order = _ . detect ( self . dataset . to _write , function ( x ) { return x . id === id ; } ) ;
if ( alter _order ) {
2011-06-28 08:30:50 +00:00
return commands . update ( alter _order . id , alter _order . values ) ;
2011-06-27 09:05:32 +00:00
}
2011-06-28 08:30:50 +00:00
return commands . link _to ( id ) ;
2011-06-29 15:35:26 +00:00
} ) ) ;
2011-06-28 08:30:50 +00:00
return val . concat ( _ . map (
this . dataset . to _delete , function ( x ) {
return commands [ 'delete' ] ( x . id ) ; } ) ) ;
2011-06-21 12:12:22 +00:00
} ,
2011-11-10 11:02:06 +00:00
save _any _view : function ( ) {
2012-01-03 17:17:13 +00:00
if ( this . doing _on _change )
return false ;
2012-07-16 11:13:24 +00:00
return this . session . synchronized _mode ( _ . bind ( function ( ) {
if ( this . viewmanager && this . viewmanager . views && this . viewmanager . active _view &&
this . viewmanager . views [ this . viewmanager . active _view ] &&
this . viewmanager . views [ this . viewmanager . active _view ] . controller ) {
var view = this . viewmanager . views [ this . viewmanager . active _view ] . controller ;
if ( this . viewmanager . active _view === "form" ) {
if ( ! view . is _initialized . isResolved ( ) ) {
return false ;
}
var res = $ . when ( view . do _save ( ) ) ;
if ( ! res . isResolved ( ) && ! res . isRejected ( ) ) {
console . warn ( "Asynchronous get_value() is not supported in form view." ) ;
}
return res ;
} else if ( this . viewmanager . active _view === "list" ) {
var res = $ . when ( view . ensure _saved ( ) ) ;
if ( ! res . isResolved ( ) && ! res . isRejected ( ) ) {
console . warn ( "Asynchronous get_value() is not supported in list view." ) ;
}
return res ;
}
}
return false ;
} , this ) ) ;
2011-07-26 15:37:47 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _syntax _valid : function ( ) {
2011-10-12 09:11:47 +00:00
if ( ! this . viewmanager . views [ this . viewmanager . active _view ] )
2012-04-18 09:16:13 +00:00
return true ;
2011-09-16 13:22:28 +00:00
var view = this . viewmanager . views [ this . viewmanager . active _view ] . controller ;
2012-06-13 10:08:19 +00:00
switch ( this . viewmanager . active _view ) {
case 'form' :
return _ ( view . fields ) . chain ( )
. invoke ( 'is_valid' )
. all ( _ . identity )
. value ( ) ;
break ;
case 'list' :
return view . is _valid ( ) ;
2011-07-26 15:37:47 +00:00
}
2012-04-18 09:16:13 +00:00
return true ;
2011-07-27 13:36:28 +00:00
} ,
2011-03-30 14:00:48 +00:00
} ) ;
2012-05-16 10:16:34 +00:00
instance . web . form . One2ManyViewManager = instance . web . ViewManager . extend ( {
template : 'One2Many.viewmanager' ,
init : function ( parent , dataset , views , flags ) {
this . _super ( parent , dataset , views , _ . extend ( { } , flags , { $sidebar : false } ) ) ;
this . registry = this . registry . extend ( {
list : 'instance.web.form.One2ManyListView' ,
form : 'instance.web.form.One2ManyFormView' ,
kanban : 'instance.web.form.One2ManyKanbanView' ,
} ) ;
2012-07-10 07:39:28 +00:00
this . _ _ignore _blur = false ;
2012-05-16 10:16:34 +00:00
} ,
switch _view : function ( mode , unused ) {
2012-05-16 10:52:51 +00:00
if ( mode !== 'form' ) {
return this . _super ( mode , unused ) ;
}
var self = this ;
2012-05-16 11:58:56 +00:00
var id = self . o2m . dataset . index !== null ? self . o2m . dataset . ids [ self . o2m . dataset . index ] : null ;
2012-05-16 10:52:51 +00:00
var pop = new instance . web . form . FormOpenPopup ( self . o2m . view ) ;
pop . show _element ( self . o2m . field . relation , id , self . o2m . build _context ( ) , {
2012-06-29 15:10:01 +00:00
title : _t ( "Open: " ) + self . o2m . string ,
2012-05-25 13:40:31 +00:00
create _function : function ( data ) {
return self . o2m . dataset . create ( data ) . then ( function ( r ) {
self . o2m . dataset . set _ids ( self . o2m . dataset . ids . concat ( [ r . result ] ) ) ;
self . o2m . dataset . on _change ( ) ;
} ) ;
} ,
2012-05-16 13:01:42 +00:00
write _function : function ( id , data , options ) {
return self . o2m . dataset . write ( id , data , { } ) . then ( function ( ) {
self . o2m . reload _current _view ( ) ;
} ) ;
} ,
2012-05-16 10:52:51 +00:00
alternative _form _view : self . o2m . field . views ? self . o2m . field . views [ "form" ] : undefined ,
parent _view : self . o2m . view ,
child _name : self . o2m . name ,
read _function : function ( ) {
return self . o2m . dataset . read _ids . apply ( self . o2m . dataset , arguments ) ;
} ,
form _view _options : { 'not_interactible_on_create' : true } ,
readonly : self . o2m . get ( "effective_readonly" )
} ) ;
2012-05-25 13:40:31 +00:00
pop . on _select _elements . add _last ( function ( ) {
self . o2m . reload _current _view ( ) ;
} ) ;
2012-05-16 10:16:34 +00:00
} ,
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . One2ManyDataSet = instance . web . BufferedDataSet . extend ( {
2011-06-29 14:35:06 +00:00
get _context : function ( ) {
2012-01-03 17:17:13 +00:00
this . context = this . o2m . build _context ( [ this . o2m . name ] ) ;
2011-06-29 14:35:06 +00:00
return this . context ;
}
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . One2ManyListView = instance . web . ListView . extend ( {
2011-12-13 14:49:55 +00:00
_template : 'One2Many.listview' ,
2012-06-14 12:37:31 +00:00
init : function ( parent , dataset , view _id , options ) {
this . _super ( parent , dataset , view _id , _ . extend ( options || { } , {
2012-06-25 13:52:15 +00:00
ListType : instance . web . form . One2ManyList
2012-06-14 12:37:31 +00:00
} ) ) ;
2012-07-17 13:23:08 +00:00
this . on ( 'edit:before' , this , this . proxy ( '_before_edit' ) ) ;
2012-08-07 07:26:50 +00:00
this . on ( 'edit:after' , this , this . proxy ( '_after_edit' ) ) ;
2012-07-17 13:23:08 +00:00
this . on ( 'save:before cancel:before' , this , this . proxy ( '_before_unedit' ) ) ;
2012-07-11 14:14:02 +00:00
this . records
2012-07-17 13:23:08 +00:00
. bind ( 'add' , this . proxy ( "changed_records" ) )
. bind ( 'edit' , this . proxy ( "changed_records" ) )
. bind ( 'remove' , this . proxy ( "changed_records" ) ) ;
2012-07-10 07:39:28 +00:00
} ,
start : function ( ) {
var ret = this . _super ( ) ;
2012-08-24 18:27:07 +00:00
this . $el
2012-07-10 07:39:28 +00:00
. off ( 'mousedown.handleButtons' )
2012-07-17 13:23:08 +00:00
. on ( 'mousedown.handleButtons' , 'table button' , this . proxy ( '_button_down' ) ) ;
2012-07-10 07:39:28 +00:00
return ret ;
2012-06-14 12:37:31 +00:00
} ,
2012-07-17 13:23:08 +00:00
changed _records : function ( ) {
2012-07-11 14:14:02 +00:00
this . o2m . trigger _on _change ( ) ;
} ,
2012-06-13 10:08:19 +00:00
is _valid : function ( ) {
2012-07-09 11:04:26 +00:00
var form = this . editor . form ;
2012-06-13 15:35:55 +00:00
// If the form has not been modified, the view can only be valid
// NB: is_dirty will also be set on defaults/onchanges/whatever?
// oe_form_dirty seems to only be set on actual user actions
2012-08-24 18:27:07 +00:00
if ( ! form . $el . is ( '.oe_form_dirty' ) ) {
2012-06-13 15:35:55 +00:00
return true ;
}
2012-07-19 08:23:52 +00:00
this . o2m . _dirty _flag = true ;
2012-06-13 10:08:19 +00:00
// Otherwise validate internal form
return _ ( form . fields ) . chain ( )
. invoke ( function ( ) {
2012-07-09 11:04:26 +00:00
this . _check _css _flags ( ) ;
2012-06-13 10:08:19 +00:00
return this . is _valid ( ) ;
} )
. all ( _ . identity )
. value ( ) ;
} ,
2011-06-16 16:37:09 +00:00
do _add _record : function ( ) {
2012-07-24 17:05:50 +00:00
if ( this . editable ( ) ) {
2011-07-04 08:38:58 +00:00
this . _super . apply ( this , arguments ) ;
} else {
var self = this ;
2012-04-17 12:09:49 +00:00
var pop = new instance . web . form . SelectCreatePopup ( this ) ;
2011-12-22 09:40:12 +00:00
pop . select _element (
self . o2m . field . relation ,
{
2012-06-29 15:10:01 +00:00
title : _t ( "Create: " ) + self . o2m . string ,
2011-12-22 09:40:12 +00:00
initial _view : "form" ,
alternative _form _view : self . o2m . field . views ? self . o2m . field . views [ "form" ] : undefined ,
create _function : function ( data , callback , error _callback ) {
return self . o2m . dataset . create ( data ) . then ( function ( r ) {
self . o2m . dataset . set _ids ( self . o2m . dataset . ids . concat ( [ r . result ] ) ) ;
self . o2m . dataset . on _change ( ) ;
} ) . then ( callback , error _callback ) ;
} ,
read _function : function ( ) {
return self . o2m . dataset . read _ids . apply ( self . o2m . dataset , arguments ) ;
} ,
parent _view : self . o2m . view ,
2012-01-03 16:34:42 +00:00
child _name : self . o2m . name ,
2011-12-22 09:40:12 +00:00
form _view _options : { 'not_interactible_on_create' : true }
2011-10-10 12:45:16 +00:00
} ,
2011-12-22 09:40:12 +00:00
self . o2m . build _domain ( ) ,
self . o2m . build _context ( )
) ;
2011-08-11 16:03:47 +00:00
pop . on _select _elements . add _last ( function ( ) {
self . o2m . reload _current _view ( ) ;
2011-06-23 17:07:34 +00:00
} ) ;
2011-07-04 08:38:58 +00:00
}
2011-07-13 11:05:32 +00:00
} ,
do _activate _record : function ( index , id ) {
var self = this ;
2012-04-17 12:09:49 +00:00
var pop = new instance . web . form . FormOpenPopup ( self . o2m . view ) ;
2011-12-22 09:52:48 +00:00
pop . show _element ( self . o2m . field . relation , id , self . o2m . build _context ( ) , {
2012-06-29 15:10:01 +00:00
title : _t ( "Open: " ) + self . o2m . string ,
2012-05-16 13:01:42 +00:00
write _function : function ( id , data ) {
return self . o2m . dataset . write ( id , data , { } , function ( r ) {
self . o2m . reload _current _view ( ) ;
} ) ;
} ,
2011-07-13 11:05:32 +00:00
alternative _form _view : self . o2m . field . views ? self . o2m . field . views [ "form" ] : undefined ,
2011-08-29 12:13:30 +00:00
parent _view : self . o2m . view ,
2011-12-19 17:30:55 +00:00
child _name : self . o2m . name ,
2011-08-29 12:13:30 +00:00
read _function : function ( ) {
return self . o2m . dataset . read _ids . apply ( self . o2m . dataset , arguments ) ;
2011-09-30 15:46:12 +00:00
} ,
2011-10-31 10:43:04 +00:00
form _view _options : { 'not_interactible_on_create' : true } ,
2012-09-04 14:11:55 +00:00
readonly : ! this . is _action _enabled ( 'edit' ) || self . o2m . get ( "effective_readonly" )
2011-07-13 11:05:32 +00:00
} ) ;
2011-12-22 13:24:47 +00:00
} ,
2012-07-11 14:14:02 +00:00
do _button _action : function ( name , id , callback ) {
if ( ! _ . isNumber ( id ) ) {
instance . webclient . notification . warn (
_t ( "Action Button" ) ,
_t ( "The o2m record must be saved before an action can be used" ) ) ;
return ;
}
2012-07-09 11:01:12 +00:00
var parent _form = this . o2m . view ;
2012-07-11 14:14:02 +00:00
var self = this ;
2012-07-17 13:23:08 +00:00
this . ensure _saved ( ) . pipe ( function ( ) {
2012-07-09 11:01:12 +00:00
return parent _form . do _save ( ) ;
2012-07-10 07:39:28 +00:00
} ) . then ( function ( ) {
2012-07-17 13:23:08 +00:00
self . handle _button ( name , id , callback ) ;
2012-06-22 08:54:32 +00:00
} ) ;
2012-07-10 07:39:28 +00:00
} ,
2012-06-25 07:20:46 +00:00
2012-07-17 13:23:08 +00:00
_before _edit : function ( ) {
2012-07-10 07:39:28 +00:00
this . _ _ignore _blur = false ;
2012-07-17 13:23:08 +00:00
this . editor . form . on ( 'blurred' , this , this . _on _form _blur ) ;
2012-07-10 07:39:28 +00:00
} ,
2012-08-07 07:26:50 +00:00
_after _edit : function ( ) {
// The form's blur thing may be jiggered during the edition setup,
// potentially leading to the o2m instasaving the row. Cancel any
// blurring triggered the edition startup here
this . editor . form . widgetFocused ( ) ;
} ,
2012-07-17 13:23:08 +00:00
_before _unedit : function ( ) {
this . editor . form . off ( 'blurred' , this , this . _on _form _blur ) ;
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
} ,
2012-07-17 13:23:08 +00:00
_button _down : function ( ) {
2012-07-10 07:39:28 +00:00
// If a button is clicked (usually some sort of action button), it's
// the button's responsibility to ensure the editable list is in the
// correct state -> ignore form blurring
this . _ _ignore _blur = true ;
} ,
/ * *
* Handles blurring of the nested form ( saves the currently edited row ) ,
* unless the flag to ignore the event is set to ` ` true ` `
2012-07-18 13:46:07 +00:00
*
* Makes the internal form go away
2012-07-10 07:39:28 +00:00
* /
2012-07-17 13:23:08 +00:00
_on _form _blur : function ( ) {
2012-07-10 07:39:28 +00:00
if ( this . _ _ignore _blur ) {
this . _ _ignore _blur = false ;
return ;
2012-06-19 11:03:50 +00:00
}
2012-07-18 13:46:07 +00:00
// FIXME: why isn't there an API for this?
2012-08-24 18:27:07 +00:00
if ( this . editor . form . $el . hasClass ( 'oe_form_dirty' ) ) {
2012-07-18 13:46:07 +00:00
this . save _edition ( ) ;
return ;
}
this . cancel _edition ( ) ;
2012-07-10 07:39:28 +00:00
} ,
keyup _ENTER : function ( ) {
// blurring caused by hitting the [Return] key, should skip the
// autosave-on-blur and let the handler for [Return] do its thing (save
// the current row *anyway*, then create a new one/edit the next one)
this . _ _ignore _blur = true ;
this . _super . apply ( this , arguments ) ;
2012-08-06 14:34:10 +00:00
} ,
do _delete : function ( ids ) {
2012-08-06 15:00:02 +00:00
var self = this ;
var next = $ . when ( ) ;
var _super = this . _super ;
// handle deletion of an item which does not exist
// TODO: better handle that in the editable list?
var false _id _index = _ ( ids ) . indexOf ( false ) ;
if ( false _id _index !== - 1 ) {
ids . splice ( false _id _index , 1 ) ;
next = this . cancel _edition ( true ) ;
}
return next . pipe ( function ( ) {
// wheeee
var confirm = window . confirm ;
window . confirm = function ( ) { return true ; } ;
try {
return _super . call ( self , ids ) ;
} finally {
window . confirm = confirm ;
}
} ) ;
2012-01-17 13:28:43 +00:00
}
2011-06-16 16:37:09 +00:00
} ) ;
2012-07-26 15:55:10 +00:00
instance . web . form . One2ManyList = instance . web . ListView . List . extend ( {
pad _table _to : function ( count ) {
2012-09-04 14:30:24 +00:00
if ( ! this . view . is _action _enabled ( 'create' ) ) {
this . _super ( count ) ;
} else {
this . _super ( count > 0 ? count - 1 : 0 ) ;
}
2012-07-26 15:55:10 +00:00
// magical invocation of wtf does that do
if ( this . view . o2m . get ( 'effective_readonly' ) ) {
return ;
}
var self = this ;
var columns = _ ( this . columns ) . filter ( function ( column ) {
return column . invisible !== '1' ;
} ) . length ;
if ( this . options . selectable ) { columns ++ ; }
if ( this . options . deletable ) { columns ++ ; }
2012-08-06 13:52:48 +00:00
2012-09-04 14:30:24 +00:00
if ( ! this . view . is _action _enabled ( 'create' ) ) {
return ;
}
2012-07-26 15:55:10 +00:00
var $cell = $ ( '<td>' , {
colspan : columns ,
'class' : 'oe_form_field_one2many_list_row_add'
2012-08-06 13:55:36 +00:00
} ) . append (
$ ( '<a>' , { href : '#' } ) . text ( _t ( "Add a row" ) )
. mousedown ( function ( ) {
// FIXME: needs to be an official API somehow
if ( self . view . editor . is _editing ( ) ) {
self . view . _ _ignore _blur = true ;
}
} )
. click ( function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
// FIXME: there should also be an API for that one
if ( self . view . editor . form . _ _blur _timeout ) {
clearTimeout ( self . view . editor . form . _ _blur _timeout ) ;
self . view . editor . form . _ _blur _timeout = false ;
}
self . view . ensure _saved ( ) . then ( function ( ) {
self . view . do _add _record ( ) ;
} ) ;
} ) ) ;
2012-08-06 13:52:48 +00:00
var $padding = this . $current . find ( 'tr:not([data-id]):first' ) ;
var $newrow = $ ( '<tr>' ) . append ( $cell ) ;
if ( $padding . length ) {
$padding . before ( $newrow ) ;
} else {
this . $current . append ( $newrow )
}
2012-07-26 15:55:10 +00:00
}
} ) ;
2011-06-16 16:37:09 +00:00
2012-04-17 12:09:49 +00:00
instance . web . form . One2ManyFormView = instance . web . FormView . extend ( {
2011-12-21 16:43:35 +00:00
form _template : 'One2Many.formview' ,
on _loaded : function ( data ) {
this . _super ( data ) ;
2012-01-11 14:37:12 +00:00
var self = this ;
2012-04-18 15:17:05 +00:00
this . $buttons . find ( 'button.oe_form_button_create' ) . click ( function ( ) {
2012-01-11 14:37:12 +00:00
self . do _save ( ) . then ( self . on _button _new ) ;
} ) ;
2012-02-15 10:34:05 +00:00
} ,
do _notify _change : function ( ) {
if ( this . dataset . parent _view ) {
this . dataset . parent _view . do _notify _change ( ) ;
} else {
this . _super . apply ( this , arguments ) ;
}
2011-12-21 16:43:35 +00:00
}
} ) ;
2012-04-06 12:30:28 +00:00
var lazy _build _o2m _kanban _view = function ( ) {
2012-04-17 12:09:49 +00:00
if ( ! instance . web _kanban || instance . web . form . One2ManyKanbanView )
2012-04-06 12:30:28 +00:00
return ;
2012-04-17 12:09:49 +00:00
instance . web . form . One2ManyKanbanView = instance . web _kanban . KanbanView . extend ( {
2012-04-06 12:30:28 +00:00
} ) ;
}
2012-06-21 23:56:41 +00:00
instance . web . form . FieldMany2ManyTags = instance . web . form . AbstractField . extend ( instance . web . form . CompletionFieldMixin , instance . web . form . ReinitializeFieldMixin , {
2012-05-07 12:54:34 +00:00
template : "FieldMany2ManyTags" ,
2012-05-07 12:22:46 +00:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-05-07 15:15:48 +00:00
instance . web . form . CompletionFieldMixin . init . call ( this ) ;
this . set ( { "value" : [ ] } ) ;
2012-05-10 08:59:13 +00:00
this . _display _orderer = new instance . web . DropMisordered ( ) ;
this . _drop _shown = false ;
2012-05-07 12:22:46 +00:00
} ,
2012-05-04 11:06:15 +00:00
start : function ( ) {
2012-05-07 15:15:48 +00:00
this . _super ( ) ;
instance . web . form . ReinitializeFieldMixin . start . call ( this ) ;
this . on ( "change:value" , this , this . render _value ) ;
} ,
initialize _content : function ( ) {
if ( this . get ( "effective_readonly" ) )
return ;
2012-05-07 12:22:46 +00:00
var self = this ;
2012-08-24 18:27:07 +00:00
self . $text = $ ( "textarea" , this . $el ) ;
2012-05-07 15:15:48 +00:00
self . $text . textext ( {
2012-05-09 13:24:19 +00:00
plugins : 'tags arrow autocomplete' ,
2012-05-04 11:06:15 +00:00
autocomplete : {
render : function ( suggestion ) {
2012-05-09 13:24:19 +00:00
return $ ( '<span class="text-label"/>' ) .
data ( 'index' , suggestion [ 'index' ] ) . html ( suggestion [ 'label' ] ) ;
2012-05-04 11:06:15 +00:00
}
} ,
2012-05-07 12:22:46 +00:00
ext : {
autocomplete : {
2012-05-10 08:59:13 +00:00
selectFromDropdown : function ( ) {
2012-05-07 12:22:46 +00:00
$ ( this ) . trigger ( 'hideDropdown' ) ;
2012-05-07 15:15:48 +00:00
var index = Number ( this . selectedSuggestionElement ( ) . children ( ) . children ( ) . data ( 'index' ) ) ;
var data = self . search _result [ index ] ;
2012-05-09 15:32:46 +00:00
if ( data . id ) {
self . add _id ( data . id ) ;
} else {
data . action ( ) ;
}
2012-05-07 15:15:48 +00:00
} ,
} ,
tags : {
isTagAllowed : function ( tag ) {
if ( ! tag . name )
return false ;
return true ;
} ,
2012-05-08 15:03:11 +00:00
removeTag : function ( tag ) {
var id = tag . data ( "id" ) ;
self . set ( { "value" : _ . without ( self . get ( "value" ) , id ) } ) ;
} ,
renderTag : function ( stuff ) {
return $ . fn . textext . TextExtTags . prototype . renderTag .
call ( this , stuff ) . data ( "id" , stuff . id ) ;
} ,
2012-05-07 15:15:48 +00:00
} ,
itemManager : {
itemToString : function ( item ) {
return item . name ;
2012-05-07 12:22:46 +00:00
} ,
} ,
} ,
2012-05-04 11:06:15 +00:00
} ) . bind ( 'getSuggestions' , function ( e , data ) {
2012-05-07 12:54:34 +00:00
var _this = this ;
2012-05-07 12:22:46 +00:00
var str = ! ! data ? data . query || '' : '' ;
2012-05-07 12:54:34 +00:00
self . get _search _result ( str ) . then ( function ( result ) {
self . search _result = result ;
$ ( _this ) . trigger ( 'setSuggestions' , { result : _ . map ( result , function ( el , i ) {
return _ . extend ( el , { index : i } ) ;
} ) } ) ;
2012-05-07 12:22:46 +00:00
} ) ;
2012-05-10 08:59:13 +00:00
} ) . bind ( 'hideDropdown' , function ( ) {
self . _drop _shown = false ;
2012-05-25 12:52:39 +00:00
} ) . bind ( 'showDropdown' , function ( ) {
2012-05-10 08:59:13 +00:00
self . _drop _shown = true ;
2012-05-04 11:06:15 +00:00
} ) ;
2012-05-07 15:15:48 +00:00
self . tags = self . $text . textext ( ) [ 0 ] . tags ( ) ;
2012-08-24 18:27:07 +00:00
$ ( "textarea" , this . $el ) . focusout ( function ( ) {
2012-05-31 09:41:00 +00:00
self . $text . trigger ( "setInputData" , "" ) ;
2012-05-10 08:59:13 +00:00
} ) . keydown ( function ( e ) {
2012-08-06 07:27:32 +00:00
if ( e . which === $ . ui . keyCode . TAB && self . _drop _shown ) {
2012-05-10 08:59:13 +00:00
self . $text . textext ( ) [ 0 ] . autocomplete ( ) . selectFromDropdown ( ) ;
}
2012-05-09 16:25:56 +00:00
} ) ;
2012-05-07 15:15:48 +00:00
} ,
set _value : function ( value _ ) {
value _ = value _ || [ ] ;
if ( value _ . length >= 1 && value _ [ 0 ] instanceof Array ) {
value _ = value _ [ 0 ] [ 2 ] ;
}
this . _super ( value _ ) ;
} ,
get _value : function ( ) {
2012-05-08 15:03:11 +00:00
var tmp = [ commands . replace _with ( this . get ( "value" ) ) ] ;
2012-05-07 15:15:48 +00:00
return tmp ;
} ,
2012-05-30 09:39:16 +00:00
get _search _blacklist : function ( ) {
return this . get ( "value" ) ;
} ,
2012-05-07 15:15:48 +00:00
render _value : function ( ) {
var self = this ;
var dataset = new instance . web . DataSetStatic ( this , this . field . relation , self . view . dataset . get _context ( ) ) ;
2012-07-25 14:31:50 +00:00
var values = self . get ( "value" )
2012-05-09 15:41:24 +00:00
var handle _names = function ( data ) {
2012-05-08 10:00:14 +00:00
var indexed = { } ;
_ . each ( data , function ( el ) {
indexed [ el [ 0 ] ] = el ;
} ) ;
2012-07-25 14:31:50 +00:00
data = _ . map ( values , function ( el ) { return indexed [ el ] ; } ) ;
2012-05-07 15:15:48 +00:00
if ( ! self . get ( "effective_readonly" ) ) {
self . tags . containerElement ( ) . children ( ) . remove ( ) ;
2012-08-24 18:27:07 +00:00
$ ( "textarea" , self . $el ) . css ( "padding-left" , "3px" ) ;
2012-05-08 10:00:14 +00:00
self . tags . addTags ( _ . map ( data , function ( el ) { return { name : el [ 1 ] , id : el [ 0 ] } ; } ) ) ;
2012-05-07 15:15:48 +00:00
} else {
2012-08-24 18:27:07 +00:00
self . $el . html ( QWeb . render ( "FieldMany2ManyTag" , { elements : data } ) ) ;
2012-05-07 15:15:48 +00:00
}
2012-05-09 15:41:24 +00:00
} ;
2012-07-25 14:31:50 +00:00
if ( ! values || values . length > 0 ) {
this . _display _orderer . add ( dataset . name _get ( values ) ) . then ( handle _names ) ;
2012-05-09 15:41:24 +00:00
} else {
handle _names ( [ ] ) ;
}
2012-05-04 11:06:15 +00:00
} ,
2012-05-09 15:32:46 +00:00
add _id : function ( id ) {
this . set ( { 'value' : _ . uniq ( this . get ( 'value' ) . concat ( [ id ] ) ) } ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2012-05-04 11:06:15 +00:00
2012-03-16 10:39:31 +00:00
/ *
* TODO niv : clean those deferred stuff , it could be better
* /
2012-05-04 13:31:11 +00:00
instance . web . form . FieldMany2Many = instance . web . form . AbstractField . extend ( {
2011-07-04 15:00:17 +00:00
multi _selection : false ,
2012-03-29 10:03:08 +00:00
disable _utility _classes : true ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2011-10-26 15:23:05 +00:00
this . is _loaded = $ . Deferred ( ) ;
this . initial _is _loaded = this . is _loaded ;
2011-10-25 16:20:31 +00:00
this . is _setted = $ . Deferred ( ) ;
2011-04-22 15:54:52 +00:00
} ,
start : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $el . addClass ( 'oe_form_field oe_form_field_many2many' ) ;
2011-06-20 14:43:48 +00:00
2011-05-31 14:11:19 +00:00
var self = this ;
2011-06-20 14:43:48 +00:00
2012-05-04 13:31:11 +00:00
this . dataset = new instance . web . form . Many2ManyDataSet ( this , this . field . relation ) ;
2011-07-01 10:27:27 +00:00
this . dataset . m2m = this ;
2011-05-31 14:11:19 +00:00
this . dataset . on _unlink . add _last ( function ( ids ) {
2012-04-17 15:45:05 +00:00
self . dataset _changed ( ) ;
2011-05-31 14:11:19 +00:00
} ) ;
2012-06-12 13:15:43 +00:00
2011-10-25 16:20:31 +00:00
this . is _setted . then ( function ( ) {
self . load _view ( ) ;
2011-05-31 15:28:07 +00:00
} ) ;
2012-03-16 10:39:31 +00:00
this . is _loaded . then ( function ( ) {
self . on ( "change:effective_readonly" , self , function ( ) {
2012-03-16 10:48:59 +00:00
self . is _loaded = self . is _loaded . pipe ( function ( ) {
self . list _view . destroy ( ) ;
return $ . when ( self . load _view ( ) ) . then ( function ( ) {
self . reload _content ( ) ;
} ) ;
2012-03-16 10:39:31 +00:00
} ) ;
} ) ;
2012-06-12 13:15:43 +00:00
} ) ;
2011-04-22 15:54:52 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
value _ = value _ || [ ] ;
if ( value _ . length >= 1 && value _ [ 0 ] instanceof Array ) {
value _ = value _ [ 0 ] [ 2 ] ;
2011-06-27 09:05:32 +00:00
}
2012-04-17 14:32:27 +00:00
this . _super ( value _ ) ;
this . dataset . set _ids ( value _ ) ;
2011-06-23 10:42:22 +00:00
var self = this ;
2011-10-26 15:23:05 +00:00
self . reload _content ( ) ;
2011-10-25 16:20:31 +00:00
this . is _setted . resolve ( ) ;
2011-04-26 12:11:46 +00:00
} ,
2012-06-07 14:19:30 +00:00
get _value : function ( ) {
return [ commands . replace _with ( this . get ( 'value' ) ) ] ;
} ,
2012-06-25 13:52:15 +00:00
is _false : function ( ) {
return _ ( this . dataset . ids ) . isEmpty ( ) ;
2011-10-31 10:27:52 +00:00
} ,
2011-10-25 16:20:31 +00:00
load _view : function ( ) {
var self = this ;
2012-05-04 13:31:11 +00:00
this . list _view = new instance . web . form . Many2ManyListView ( this , this . dataset , false , {
2012-03-16 10:39:31 +00:00
'addable' : self . get ( "effective_readonly" ) ? null : _t ( "Add" ) ,
'deletable' : self . get ( "effective_readonly" ) ? false : true ,
2011-12-28 16:29:52 +00:00
'selectable' : self . multi _selection ,
2012-06-11 15:28:19 +00:00
'sortable' : false ,
2012-06-18 14:38:17 +00:00
'reorderable' : false ,
2012-07-02 16:09:51 +00:00
'import_enabled' : false ,
2011-10-25 16:20:31 +00:00
} ) ;
2011-11-10 14:07:54 +00:00
var embedded = ( this . field . views || { } ) . tree ;
if ( embedded ) {
this . list _view . set _embedded _view ( embedded ) ;
}
2011-10-25 16:20:31 +00:00
this . list _view . m2m _field = this ;
var loaded = $ . Deferred ( ) ;
this . list _view . on _loaded . add _last ( function ( ) {
2011-10-26 15:23:05 +00:00
self . initial _is _loaded . resolve ( ) ;
2011-10-25 16:20:31 +00:00
loaded . resolve ( ) ;
} ) ;
2011-12-19 16:28:05 +00:00
$ . async _when ( ) . then ( function ( ) {
2012-08-24 18:27:07 +00:00
self . list _view . appendTo ( self . $el ) ;
2011-12-19 16:28:05 +00:00
} ) ;
2011-10-25 16:20:31 +00:00
return loaded ;
} ,
2011-10-26 15:23:05 +00:00
reload _content : function ( ) {
var self = this ;
this . is _loaded = this . is _loaded . pipe ( function ( ) {
return self . list _view . reload _content ( ) ;
} ) ;
} ,
2012-04-17 15:45:05 +00:00
dataset _changed : function ( ) {
2012-06-07 14:19:30 +00:00
this . set ( { 'value' : this . dataset . ids } ) ;
2012-04-17 15:45:05 +00:00
} ,
2011-07-01 10:27:27 +00:00
} ) ;
2012-05-04 13:31:11 +00:00
instance . web . form . Many2ManyDataSet = instance . web . DataSetStatic . extend ( {
2011-07-01 10:27:27 +00:00
get _context : function ( ) {
this . context = this . m2m . build _context ( ) ;
return this . context ;
2011-03-30 14:00:48 +00:00
}
} ) ;
2011-09-12 11:43:50 +00:00
/ * *
* @ class
2012-04-17 12:09:49 +00:00
* @ extends instance . web . ListView
2011-09-12 11:43:50 +00:00
* /
2012-05-04 13:31:11 +00:00
instance . web . form . Many2ManyListView = instance . web . ListView . extend ( /** @lends instance.web.form.Many2ManyListView# */ {
2011-05-27 15:10:00 +00:00
do _add _record : function ( ) {
2012-04-17 12:09:49 +00:00
var pop = new instance . web . form . SelectCreatePopup ( this ) ;
2011-12-22 09:40:12 +00:00
pop . select _element (
this . model ,
{
2012-06-29 15:10:01 +00:00
title : _t ( "Add: " ) + this . m2m _field . string
2011-12-22 09:40:12 +00:00
} ,
2012-04-17 12:09:49 +00:00
new instance . web . CompoundDomain ( this . m2m _field . build _domain ( ) , [ "!" , [ "id" , "in" , this . m2m _field . dataset . ids ] ] ) ,
2011-12-22 09:40:12 +00:00
this . m2m _field . build _context ( )
) ;
2011-05-09 11:52:33 +00:00
var self = this ;
2011-06-15 15:04:40 +00:00
pop . on _select _elements . add ( function ( element _ids ) {
2012-03-19 16:41:31 +00:00
_ . each ( element _ids , function ( one _id ) {
if ( ! _ . detect ( self . dataset . ids , function ( x ) { return x == one _id ; } ) ) {
self . dataset . set _ids ( [ ] . concat ( self . dataset . ids , [ one _id ] ) ) ;
2012-04-17 15:45:05 +00:00
self . m2m _field . dataset _changed ( ) ;
2011-06-15 15:04:40 +00:00
self . reload _content ( ) ;
}
} ) ;
2011-05-09 11:52:33 +00:00
} ) ;
2011-04-26 14:13:34 +00:00
} ,
2011-05-27 15:10:00 +00:00
do _activate _record : function ( index , id ) {
2011-07-13 09:59:49 +00:00
var self = this ;
2012-04-17 12:09:49 +00:00
var pop = new instance . web . form . FormOpenPopup ( this ) ;
2011-12-22 09:40:12 +00:00
pop . show _element ( this . dataset . model , id , this . m2m _field . build _context ( ) , {
2012-06-29 15:10:01 +00:00
title : _t ( "Open: " ) + this . m2m _field . string ,
2012-03-16 10:39:31 +00:00
readonly : this . getParent ( ) . get ( "effective_readonly" )
2011-12-22 09:40:12 +00:00
} ) ;
2011-07-13 09:59:49 +00:00
pop . on _write _completed . add _last ( function ( ) {
self . reload _content ( ) ;
2011-04-26 14:13:34 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
}
} ) ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldMany2ManyKanban = instance . web . form . AbstractField . extend ( instance . web . form . CompletionFieldMixin , {
2012-05-24 12:59:35 +00:00
disable _utility _classes : true ,
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2012-05-25 12:52:39 +00:00
instance . web . form . CompletionFieldMixin . init . call ( this ) ;
2012-05-24 12:59:35 +00:00
m2m _kanban _lazy _init ( ) ;
this . is _loaded = $ . Deferred ( ) ;
this . initial _is _loaded = this . is _loaded ;
this . is _setted = $ . Deferred ( ) ;
} ,
start : function ( ) {
this . _super . apply ( this , arguments ) ;
var self = this ;
this . dataset = new instance . web . form . Many2ManyDataSet ( this , this . field . relation ) ;
this . dataset . m2m = this ;
this . dataset . on _unlink . add _last ( function ( ids ) {
self . dataset _changed ( ) ;
} ) ;
2012-07-12 12:56:37 +00:00
2012-05-24 12:59:35 +00:00
this . is _setted . then ( function ( ) {
self . load _view ( ) ;
} ) ;
this . is _loaded . then ( function ( ) {
self . on ( "change:effective_readonly" , self , function ( ) {
self . is _loaded = self . is _loaded . pipe ( function ( ) {
self . kanban _view . destroy ( ) ;
return $ . when ( self . load _view ( ) ) . then ( function ( ) {
self . reload _content ( ) ;
} ) ;
} ) ;
} ) ;
} )
} ,
set _value : function ( value _ ) {
value _ = value _ || [ ] ;
if ( value _ . length >= 1 && value _ [ 0 ] instanceof Array ) {
value _ = value _ [ 0 ] [ 2 ] ;
}
this . _super ( value _ ) ;
this . dataset . set _ids ( value _ ) ;
var self = this ;
self . reload _content ( ) ;
this . is _setted . resolve ( ) ;
} ,
load _view : function ( ) {
var self = this ;
this . kanban _view = new instance . web . form . Many2ManyKanbanView ( this , this . dataset , false , {
'create_text' : _t ( "Add" ) ,
'creatable' : self . get ( "effective_readonly" ) ? false : true ,
'quick_creatable' : self . get ( "effective_readonly" ) ? false : true ,
2012-06-04 10:37:35 +00:00
'read_only_mode' : self . get ( "effective_readonly" ) ? true : false ,
'confirm_on_delete' : false ,
2012-05-24 12:59:35 +00:00
} ) ;
var embedded = ( this . field . views || { } ) . kanban ;
if ( embedded ) {
this . kanban _view . set _embedded _view ( embedded ) ;
}
2012-05-25 12:52:39 +00:00
this . kanban _view . m2m = this ;
2012-05-24 12:59:35 +00:00
var loaded = $ . Deferred ( ) ;
this . kanban _view . on _loaded . add _last ( function ( ) {
self . initial _is _loaded . resolve ( ) ;
loaded . resolve ( ) ;
} ) ;
2012-05-24 14:42:51 +00:00
this . kanban _view . do _switch _view . add _last ( _ . bind ( this . open _popup , this ) ) ;
2012-05-24 12:59:35 +00:00
$ . async _when ( ) . then ( function ( ) {
2012-08-24 18:27:07 +00:00
self . kanban _view . appendTo ( self . $el ) ;
2012-05-24 12:59:35 +00:00
} ) ;
return loaded ;
} ,
reload _content : function ( ) {
var self = this ;
this . is _loaded = this . is _loaded . pipe ( function ( ) {
return self . kanban _view . do _search ( self . build _domain ( ) , self . dataset . get _context ( ) , [ ] ) ;
} ) ;
} ,
dataset _changed : function ( ) {
this . set ( { 'value' : [ commands . replace _with ( this . dataset . ids ) ] } ) ;
} ,
2012-05-24 14:42:51 +00:00
open _popup : function ( type , unused ) {
2012-05-24 12:59:35 +00:00
if ( type !== "form" )
return ;
var self = this ;
2012-05-24 14:42:51 +00:00
if ( this . dataset . index === null ) {
var pop = new instance . web . form . SelectCreatePopup ( this ) ;
pop . select _element (
this . field . relation ,
{
2012-06-29 15:10:01 +00:00
title : _t ( "Add: " ) + this . string
2012-05-24 14:42:51 +00:00
} ,
new instance . web . CompoundDomain ( this . build _domain ( ) , [ "!" , [ "id" , "in" , this . dataset . ids ] ] ) ,
this . build _context ( )
) ;
pop . on _select _elements . add ( function ( element _ids ) {
_ . each ( element _ids , function ( one _id ) {
if ( ! _ . detect ( self . dataset . ids , function ( x ) { return x == one _id ; } ) ) {
self . dataset . set _ids ( [ ] . concat ( self . dataset . ids , [ one _id ] ) ) ;
self . dataset _changed ( ) ;
self . reload _content ( ) ;
}
} ) ;
2012-05-24 12:59:35 +00:00
} ) ;
2012-05-24 14:42:51 +00:00
} else {
var id = self . dataset . ids [ self . dataset . index ] ;
var pop = new instance . web . form . FormOpenPopup ( self . view ) ;
pop . show _element ( self . field . relation , id , self . build _context ( ) , {
2012-06-29 15:10:01 +00:00
title : _t ( "Open: " ) + self . string ,
2012-05-24 14:42:51 +00:00
write _function : function ( id , data , options ) {
return self . dataset . write ( id , data , { } ) . then ( function ( ) {
self . reload _content ( ) ;
} ) ;
} ,
alternative _form _view : self . field . views ? self . field . views [ "form" ] : undefined ,
parent _view : self . view ,
child _name : self . name ,
readonly : self . get ( "effective_readonly" )
} ) ;
}
2012-05-24 12:59:35 +00:00
} ,
2012-05-25 13:09:14 +00:00
add _id : function ( id ) {
this . quick _create . add _id ( id ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2012-05-24 12:59:35 +00:00
function m2m _kanban _lazy _init ( ) {
if ( instance . web . form . Many2ManyKanbanView )
return ;
instance . web . form . Many2ManyKanbanView = instance . web _kanban . KanbanView . extend ( {
2012-05-25 12:52:39 +00:00
quick _create _class : 'instance.web.form.Many2ManyQuickCreate' ,
2012-05-25 12:09:47 +00:00
_is _quick _create _enabled : function ( ) {
return this . _super ( ) && ! this . group _by ;
} ,
} ) ;
instance . web . form . Many2ManyQuickCreate = instance . web . Widget . extend ( {
template : 'Many2ManyKanban.quick_create' ,
2012-07-12 12:56:37 +00:00
2012-05-25 12:09:47 +00:00
/ * *
* close _btn : If true , the widget will display a "Close" button able to trigger
* a "close" event .
* /
init : function ( parent , dataset , context , buttons ) {
this . _super ( parent ) ;
this . m2m = this . getParent ( ) . view . m2m ;
2012-05-25 13:09:14 +00:00
this . m2m . quick _create = this ;
2012-05-25 12:09:47 +00:00
this . _dataset = dataset ;
this . _buttons = buttons || false ;
this . _context = context || { } ;
} ,
start : function ( ) {
var self = this ;
2012-08-24 18:27:07 +00:00
self . $text = this . $el . find ( 'input' ) . css ( "width" , "200px" ) ;
2012-05-25 12:52:39 +00:00
self . $text . textext ( {
plugins : 'arrow autocomplete' ,
autocomplete : {
render : function ( suggestion ) {
return $ ( '<span class="text-label"/>' ) .
data ( 'index' , suggestion [ 'index' ] ) . html ( suggestion [ 'label' ] ) ;
}
} ,
ext : {
autocomplete : {
selectFromDropdown : function ( ) {
$ ( this ) . trigger ( 'hideDropdown' ) ;
var index = Number ( this . selectedSuggestionElement ( ) . children ( ) . children ( ) . data ( 'index' ) ) ;
var data = self . search _result [ index ] ;
if ( data . id ) {
self . add _id ( data . id ) ;
} else {
data . action ( ) ;
}
} ,
} ,
itemManager : {
itemToString : function ( item ) {
return item . name ;
} ,
} ,
} ,
} ) . bind ( 'getSuggestions' , function ( e , data ) {
var _this = this ;
var str = ! ! data ? data . query || '' : '' ;
self . m2m . get _search _result ( str ) . then ( function ( result ) {
self . search _result = result ;
$ ( _this ) . trigger ( 'setSuggestions' , { result : _ . map ( result , function ( el , i ) {
return _ . extend ( el , { index : i } ) ;
} ) } ) ;
} ) ;
} ) ;
self . $text . focusout ( function ( ) {
self . $text . val ( "" ) ;
} ) ;
2012-05-25 12:09:47 +00:00
} ,
focus : function ( ) {
2012-05-25 13:09:14 +00:00
this . $text . focus ( ) ;
2012-05-25 12:09:47 +00:00
} ,
2012-05-25 12:52:39 +00:00
add _id : function ( id ) {
2012-05-25 13:09:14 +00:00
var self = this ;
self . $text . val ( "" ) ;
2012-05-25 12:52:39 +00:00
self . trigger ( 'added' , id ) ;
2012-06-04 12:36:09 +00:00
this . m2m . dataset _changed ( ) ;
2012-05-25 12:52:39 +00:00
} ,
2012-05-24 12:59:35 +00:00
} ) ;
}
2012-05-16 14:06:22 +00:00
/ * *
* Class with everything which is common between FormOpenPopup and SelectCreatePopup .
* /
2012-08-14 13:42:46 +00:00
instance . web . form . AbstractFormPopup = instance . web . Widget . extend ( {
2012-05-16 11:58:56 +00:00
template : "AbstractFormPopup.render" ,
/ * *
* options :
2012-05-16 13:01:42 +00:00
* - readonly : only applicable when not in creation mode , default to false
2012-05-16 14:06:22 +00:00
* - alternative _form _view
* - write _function
* - read _function
* - create _function
* - parent _view
* - child _name
* - form _view _options
2012-05-16 11:58:56 +00:00
* /
init _popup : function ( model , row _id , domain , context , options ) {
this . row _id = row _id ;
this . model = model ;
this . domain = domain || [ ] ;
this . context = context || { } ;
this . options = options ;
_ . defaults ( this . options , {
} ) ;
} ,
2012-05-16 13:01:42 +00:00
init _dataset : function ( ) {
var self = this ;
2012-05-16 14:01:19 +00:00
this . created _elements = [ ] ;
2012-05-16 13:01:42 +00:00
this . dataset = new instance . web . ProxyDataSet ( this , this . model , this . context ) ;
this . dataset . read _function = this . options . read _function ;
this . dataset . create _function = function ( data , sup ) {
var fct = self . options . create _function || sup ;
return fct . call ( this , data ) . then ( function ( r ) {
self . created _elements . push ( r . result ) ;
} ) ;
} ;
this . dataset . write _function = function ( id , data , options , sup ) {
var fct = self . options . write _function || sup ;
2012-05-16 13:05:10 +00:00
return fct . call ( this , id , data , options ) . then ( self . on _write _completed ) ;
2012-05-16 13:01:42 +00:00
} ;
this . dataset . parent _view = this . options . parent _view ;
this . dataset . child _name = this . options . child _name ;
} ,
display _popup : function ( ) {
2012-05-16 14:01:19 +00:00
var self = this ;
2012-05-16 13:01:42 +00:00
this . renderElement ( ) ;
2012-07-20 12:35:28 +00:00
var dialog = new instance . web . Dialog ( this , {
2012-05-16 14:13:28 +00:00
min _width : '800px' ,
2012-07-20 12:35:28 +00:00
dialogClass : 'oe_act_window' ,
2012-05-16 13:01:42 +00:00
close : function ( ) {
2012-06-04 12:36:09 +00:00
self . check _exit ( true ) ;
2012-05-16 13:01:42 +00:00
} ,
2012-05-16 14:13:28 +00:00
title : this . options . title || "" ,
2012-07-20 12:35:28 +00:00
buttons : [ { text : "tmp" } ] ,
2012-08-24 18:27:07 +00:00
} , this . $el ) . open ( ) ;
this . $buttonpane = dialog . $el . dialog ( "widget" ) . find ( ".ui-dialog-buttonpane" ) . html ( "" ) ;
2012-05-16 13:01:42 +00:00
this . start ( ) ;
} ,
on _write _completed : function ( ) { } ,
2012-05-16 11:58:56 +00:00
setup _form _view : function ( ) {
var self = this ;
2012-05-16 14:01:19 +00:00
if ( this . row _id ) {
this . dataset . ids = [ this . row _id ] ;
this . dataset . index = 0 ;
} else {
this . dataset . index = null ;
}
2012-05-16 11:58:56 +00:00
var options = _ . clone ( self . options . form _view _options ) || { } ;
if ( this . row _id !== null ) {
options . initial _mode = this . options . readonly ? "view" : "edit" ;
}
2012-07-20 12:35:28 +00:00
_ . extend ( options , {
$buttons : this . $buttonpane ,
} ) ;
2012-05-16 11:58:56 +00:00
this . view _form = new instance . web . FormView ( this , this . dataset , false , options ) ;
if ( this . options . alternative _form _view ) {
this . view _form . set _embedded _view ( this . options . alternative _form _view ) ;
}
2012-08-24 18:27:07 +00:00
this . view _form . appendTo ( this . $el . find ( ".oe_popup_form" ) ) ;
2012-05-16 11:58:56 +00:00
this . view _form . on _loaded . add _last ( function ( ) {
var multi _select = self . row _id === null && ! self . options . disable _multiple _selection ;
2012-08-10 14:50:48 +00:00
self . $buttonpane . html ( QWeb . render ( "AbstractFormPopup.buttons" , {
multi _select : multi _select ,
readonly : self . row _id !== null && self . options . readonly ,
} ) ) ;
2012-07-20 12:35:28 +00:00
var $snbutton = self . $buttonpane . find ( ".oe_abstractformpopup-form-save-new" ) ;
2012-05-16 11:58:56 +00:00
$snbutton . click ( function ( ) {
$ . when ( self . view _form . do _save ( ) ) . then ( function ( ) {
self . view _form . reload _mutex . exec ( function ( ) {
self . view _form . on _button _new ( ) ;
} ) ;
} ) ;
} ) ;
2012-07-20 12:35:28 +00:00
var $sbutton = self . $buttonpane . find ( ".oe_abstractformpopup-form-save" ) ;
2012-05-16 11:58:56 +00:00
$sbutton . click ( function ( ) {
$ . when ( self . view _form . do _save ( ) ) . then ( function ( ) {
self . view _form . reload _mutex . exec ( function ( ) {
self . check _exit ( ) ;
} ) ;
} ) ;
} ) ;
2012-07-20 12:35:28 +00:00
var $cbutton = self . $buttonpane . find ( ".oe_abstractformpopup-form-close" ) ;
2012-05-16 11:58:56 +00:00
$cbutton . click ( function ( ) {
self . check _exit ( ) ;
} ) ;
self . view _form . do _show ( ) ;
} ) ;
} ,
2012-05-16 14:01:19 +00:00
on _select _elements : function ( element _ids ) {
} ,
2012-06-04 12:36:09 +00:00
check _exit : function ( no _destroy ) {
2012-05-16 14:01:19 +00:00
if ( this . created _elements . length > 0 ) {
this . on _select _elements ( this . created _elements ) ;
2012-06-04 12:36:09 +00:00
this . created _elements = [ ] ;
2012-05-16 14:01:19 +00:00
}
2012-05-16 11:58:56 +00:00
this . destroy ( ) ;
} ,
2012-05-16 14:01:19 +00:00
destroy : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . dialog ( 'close' ) ;
2012-05-16 14:01:19 +00:00
this . _super ( ) ;
} ,
2012-05-16 11:58:56 +00:00
} ) ;
2011-09-12 11:43:50 +00:00
/ * *
2012-05-16 14:06:22 +00:00
* Class to display a popup containing a form view .
2011-09-12 11:43:50 +00:00
* /
2012-05-16 14:06:22 +00:00
instance . web . form . FormOpenPopup = instance . web . form . AbstractFormPopup . extend ( {
show _element : function ( model , row _id , context , options ) {
this . init _popup ( model , row _id , [ ] , context , options ) ;
_ . defaults ( this . options , {
} ) ;
this . display _popup ( ) ;
} ,
start : function ( ) {
this . _super ( ) ;
this . init _dataset ( ) ;
this . setup _form _view ( ) ;
} ,
} ) ;
/ * *
* Class to display a popup to display a list to search a row . It also allows
* to switch to a form view to create a new row .
* /
instance . web . form . SelectCreatePopup = instance . web . form . AbstractFormPopup . extend ( {
2011-06-15 15:04:40 +00:00
/ * *
* options :
* - initial _ids
* - initial _view : form or search ( default search )
* - disable _multiple _selection
2011-09-30 15:46:12 +00:00
* - list _view _options
2011-06-15 15:04:40 +00:00
* /
2011-06-17 08:41:33 +00:00
select _element : function ( model , options , domain , context ) {
2012-05-16 13:01:42 +00:00
this . init _popup ( model , null , domain , context , options ) ;
2011-08-11 16:03:47 +00:00
var self = this ;
2012-05-16 11:58:56 +00:00
_ . defaults ( this . options , {
2012-05-16 13:01:42 +00:00
initial _view : "search" ,
2012-05-16 11:58:56 +00:00
} ) ;
2011-06-15 15:04:40 +00:00
this . initial _ids = this . options . initial _ids ;
2012-05-16 13:01:42 +00:00
this . display _popup ( ) ;
2011-05-09 10:05:41 +00:00
} ,
start : function ( ) {
2011-10-10 09:44:38 +00:00
var self = this ;
2012-05-16 13:01:42 +00:00
this . init _dataset ( ) ;
2011-06-23 16:57:17 +00:00
if ( this . options . initial _view == "search" ) {
2011-11-23 10:30:00 +00:00
self . rpc ( '/web/session/eval_domain_and_context' , {
2011-11-24 10:43:05 +00:00
domains : [ ] ,
contexts : [ this . context ]
} , function ( results ) {
var search _defaults = { } ;
2012-04-17 14:32:27 +00:00
_ . each ( results . context , function ( value _ , key ) {
2011-11-24 10:43:05 +00:00
var match = /^search_default_(.*)$/ . exec ( key ) ;
if ( match ) {
2012-04-17 14:32:27 +00:00
search _defaults [ match [ 1 ] ] = value _ ;
2011-11-24 10:43:05 +00:00
}
} ) ;
self . setup _search _view ( search _defaults ) ;
2011-11-23 10:30:00 +00:00
} ) ;
2011-06-09 13:20:04 +00:00
} else { // "form"
this . new _object ( ) ;
2011-05-16 11:02:42 +00:00
}
2011-05-09 10:05:41 +00:00
} ,
2011-11-22 10:41:45 +00:00
setup _search _view : function ( search _defaults ) {
2011-05-09 10:05:41 +00:00
var self = this ;
if ( this . searchview ) {
2012-02-21 16:29:12 +00:00
this . searchview . destroy ( ) ;
2011-05-09 10:05:41 +00:00
}
2012-04-17 12:09:49 +00:00
this . searchview = new instance . web . SearchView ( this ,
2011-11-22 10:41:45 +00:00
this . dataset , false , search _defaults ) ;
2011-05-09 11:52:33 +00:00
this . searchview . on _search . add ( function ( domains , contexts , groupbys ) {
2011-06-09 13:20:04 +00:00
if ( self . initial _ids ) {
2011-10-06 09:22:47 +00:00
self . do _search ( domains . concat ( [ [ [ "id" , "in" , self . initial _ids ] ] , self . domain ] ) ,
2011-06-17 14:19:45 +00:00
contexts , groupbys ) ;
2011-06-09 13:20:04 +00:00
self . initial _ids = undefined ;
} else {
2012-01-27 11:20:50 +00:00
self . do _search ( domains . concat ( [ self . domain ] ) , contexts . concat ( self . context ) , groupbys ) ;
2011-06-09 13:20:04 +00:00
}
2011-05-09 11:52:33 +00:00
} ) ;
2011-05-10 10:30:46 +00:00
this . searchview . on _loaded . add _last ( function ( ) {
2012-04-17 12:09:49 +00:00
self . view _list = new instance . web . form . SelectCreateListView ( self ,
2011-09-14 15:22:08 +00:00
self . dataset , false ,
2011-10-31 15:49:07 +00:00
_ . extend ( { 'deletable' : false ,
2012-06-07 15:23:01 +00:00
'selectable' : ! self . options . disable _multiple _selection ,
2012-07-02 16:09:51 +00:00
'import_enabled' : false ,
2012-07-20 12:35:28 +00:00
'$buttons' : self . $buttonpane ,
2011-10-31 15:49:07 +00:00
} , self . options . list _view _options || { } ) ) ;
2012-07-24 17:05:50 +00:00
self . view _list . on ( 'edit:before' , self , function ( e ) {
e . cancel = true ;
} ) ;
2011-05-10 12:15:51 +00:00
self . view _list . popup = self ;
2012-08-24 18:27:07 +00:00
self . view _list . appendTo ( $ ( ".oe_popup_list" , self . $el ) ) . pipe ( function ( ) {
2011-09-15 10:08:08 +00:00
self . view _list . do _show ( ) ;
} ) . pipe ( function ( ) {
2011-06-09 13:20:04 +00:00
self . searchview . do _search ( ) ;
2011-05-10 12:15:51 +00:00
} ) ;
2011-12-28 15:53:33 +00:00
self . view _list . on _loaded . add _last ( function ( ) {
2012-07-20 12:44:30 +00:00
self . $buttonpane . html ( QWeb . render ( "SelectCreatePopup.search.buttons" , { widget : self } ) ) ;
2012-07-20 12:35:28 +00:00
var $cbutton = self . $buttonpane . find ( ".oe_selectcreatepopup-search-close" ) ;
2011-12-28 15:53:33 +00:00
$cbutton . click ( function ( ) {
2012-02-21 16:29:12 +00:00
self . destroy ( ) ;
2011-12-28 15:53:33 +00:00
} ) ;
2012-07-20 12:35:28 +00:00
var $sbutton = self . $buttonpane . find ( ".oe_selectcreatepopup-search-select" ) ;
2011-12-28 15:53:33 +00:00
$sbutton . click ( function ( ) {
self . on _select _elements ( self . selected _ids ) ;
2012-02-21 16:29:12 +00:00
self . destroy ( ) ;
2011-12-28 15:53:33 +00:00
} ) ;
} ) ;
2011-05-10 10:30:46 +00:00
} ) ;
2012-08-24 18:27:07 +00:00
this . searchview . appendTo ( $ ( ".oe_popup_list" , self . $el ) ) ;
2011-05-09 10:05:41 +00:00
} ,
2011-10-06 09:22:47 +00:00
do _search : function ( domains , contexts , groupbys ) {
var self = this ;
this . rpc ( '/web/session/eval_domain_and_context' , {
domains : domains || [ ] ,
contexts : contexts || [ ] ,
group _by _seq : groupbys || [ ]
} , function ( results ) {
self . view _list . do _search ( results . domain , results . context , results . group _by ) ;
} ) ;
} ,
2011-06-15 15:04:40 +00:00
on _click _element : function ( ids ) {
2012-08-10 14:22:53 +00:00
var self = this ;
2011-06-15 15:04:40 +00:00
this . selected _ids = ids || [ ] ;
if ( this . selected _ids . length > 0 ) {
2012-08-10 14:22:53 +00:00
self . $buttonpane . find ( ".oe_selectcreatepopup-search-select" ) . removeAttr ( 'disabled' ) ;
2011-06-15 15:04:40 +00:00
} else {
2012-08-10 14:22:53 +00:00
self . $buttonpane . find ( ".oe_selectcreatepopup-search-select" ) . attr ( 'disabled' , "disabled" ) ;
2011-06-15 15:04:40 +00:00
}
2011-05-10 10:30:46 +00:00
} ,
new _object : function ( ) {
2011-06-09 13:20:04 +00:00
if ( this . searchview ) {
this . searchview . hide ( ) ;
}
if ( this . view _list ) {
2012-08-24 18:27:07 +00:00
this . view _list . $el . hide ( ) ;
2011-06-09 13:20:04 +00:00
}
2012-05-16 11:58:56 +00:00
this . setup _form _view ( ) ;
2011-08-11 16:03:47 +00:00
} ,
2011-05-03 16:17:25 +00:00
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . SelectCreateListView = instance . web . ListView . extend ( {
2011-06-15 15:06:58 +00:00
do _add _record : function ( ) {
this . popup . new _object ( ) ;
} ,
2011-05-24 08:35:58 +00:00
select _record : function ( index ) {
2011-06-15 15:04:40 +00:00
this . popup . on _select _elements ( [ this . dataset . ids [ index ] ] ) ;
2012-02-21 16:29:12 +00:00
this . popup . destroy ( ) ;
2011-06-15 15:04:40 +00:00
} ,
do _select : function ( ids , records ) {
this . _super ( ids , records ) ;
this . popup . on _click _element ( ids ) ;
2011-05-09 11:52:33 +00:00
}
} ) ;
2012-06-21 23:56:41 +00:00
instance . web . form . FieldReference = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-03-15 16:21:07 +00:00
template : 'FieldReference' ,
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
2011-12-08 12:28:51 +00:00
this . reference _ready = true ;
2011-08-18 17:45:08 +00:00
} ,
on _nop : function ( ) {
} ,
on _selection _changed : function ( ) {
2011-12-08 12:28:51 +00:00
if ( this . reference _ready ) {
var sel = this . selection . get _value ( ) ;
this . m2o . field . relation = sel ;
2012-04-17 15:45:05 +00:00
this . m2o . set _value ( false ) ;
2012-08-24 18:27:07 +00:00
this . m2o . $el . toggle ( sel !== false ) ;
2011-12-08 12:28:51 +00:00
}
2011-08-18 17:45:08 +00:00
} ,
2012-03-20 10:17:47 +00:00
destroy _content : function ( ) {
2012-03-19 13:09:52 +00:00
if ( this . selection ) {
2012-03-20 09:58:19 +00:00
this . selection . destroy ( ) ;
2012-03-19 13:09:52 +00:00
this . selection = undefined ;
}
if ( this . m2o ) {
2012-03-20 09:58:19 +00:00
this . m2o . destroy ( ) ;
2012-04-10 11:58:08 +00:00
this . m2o = undefined ;
2012-03-19 13:09:52 +00:00
}
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
var self = this ;
2012-04-17 12:09:49 +00:00
this . selection = new instance . web . form . FieldSelection ( this , { attrs : {
2012-04-20 09:20:07 +00:00
name : 'selection'
2012-04-10 11:58:08 +00:00
} } ) ;
2012-04-20 09:20:07 +00:00
this . selection . view = this . view ;
2012-04-20 09:40:34 +00:00
this . selection . set ( { force _readonly : this . get ( 'effective_readonly' ) } ) ;
2012-04-17 15:45:05 +00:00
this . selection . on ( "change:value" , this , this . on _selection _changed ) ;
2012-07-26 14:45:33 +00:00
this . selection . setElement ( this . $ ( ".oe_form_view_reference_selection" ) ) ;
2012-02-29 11:07:44 +00:00
this . selection . renderElement ( ) ;
2011-08-18 17:45:08 +00:00
this . selection . start ( ) ;
2012-06-25 14:40:13 +00:00
this . selection
. on ( 'focused' , null , function ( ) { self . trigger ( 'focused' ) } )
. on ( 'blurred' , null , function ( ) { self . trigger ( 'blurred' ) } ) ;
2012-04-10 11:58:08 +00:00
2012-04-17 12:09:49 +00:00
this . m2o = new instance . web . form . FieldMany2One ( this , { attrs : {
2012-04-20 09:20:07 +00:00
name : 'm2o'
2012-03-19 13:09:52 +00:00
} } ) ;
2012-04-20 09:20:07 +00:00
this . m2o . view = this . view ;
2012-04-20 09:40:34 +00:00
this . m2o . set ( { force _readonly : this . get ( "effective_readonly" ) } ) ;
2012-04-17 15:45:05 +00:00
this . m2o . on ( "change:value" , this , this . data _changed ) ;
2012-07-26 14:45:33 +00:00
this . m2o . setElement ( this . $ ( ".oe_form_view_reference_m2o" ) ) ;
2012-02-29 11:07:44 +00:00
this . m2o . renderElement ( ) ;
2011-08-18 17:45:08 +00:00
this . m2o . start ( ) ;
2012-06-25 14:40:13 +00:00
this . m2o
. on ( 'focused' , null , function ( ) { self . trigger ( 'focused' ) } )
. on ( 'blurred' , null , function ( ) { self . trigger ( 'blurred' ) } ) ;
2011-08-18 17:45:08 +00:00
} ,
2012-04-18 09:50:43 +00:00
is _false : function ( ) {
return typeof ( this . get _value ( ) ) !== 'string' ;
2011-08-18 17:45:08 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
this . _super ( value _ ) ;
2012-03-19 13:09:52 +00:00
this . render _value ( ) ;
2011-08-18 17:45:08 +00:00
} ,
2012-03-19 13:09:52 +00:00
render _value : function ( ) {
2011-12-08 12:28:51 +00:00
this . reference _ready = false ;
var vals = [ ] , sel _val , m2o _val ;
2012-04-17 14:32:27 +00:00
if ( typeof ( this . get ( 'value' ) ) === 'string' ) {
vals = this . get ( 'value' ) . split ( ',' ) ;
2011-08-18 17:45:08 +00:00
}
2011-12-08 12:28:51 +00:00
sel _val = vals [ 0 ] || false ;
2012-04-20 09:40:34 +00:00
m2o _val = vals [ 1 ] ? parseInt ( vals [ 1 ] , 10 ) : vals [ 1 ] ;
2012-03-19 13:09:52 +00:00
if ( ! this . get ( "effective_readonly" ) ) {
this . selection . set _value ( sel _val ) ;
}
2011-12-08 12:28:51 +00:00
this . m2o . field . relation = sel _val ;
this . m2o . set _value ( m2o _val ) ;
this . reference _ready = true ;
2011-08-18 17:45:08 +00:00
} ,
2012-04-17 15:45:05 +00:00
data _changed : function ( ) {
2011-08-18 17:45:08 +00:00
var model = this . selection . get _value ( ) ,
id = this . m2o . get _value ( ) ;
if ( typeof ( model ) === 'string' && typeof ( id ) === 'number' ) {
2012-04-17 15:45:05 +00:00
this . set ( { 'value' : model + ',' + id } ) ;
2011-08-18 17:45:08 +00:00
} else {
2012-04-17 15:45:05 +00:00
this . set ( { 'value' : false } ) ;
2011-08-18 17:45:08 +00:00
}
2012-04-17 15:45:05 +00:00
} ,
2012-04-20 09:20:07 +00:00
get _field : function ( name ) {
if ( name === "selection" ) {
return {
selection : this . view . fields _view . fields [ this . name ] . selection ,
type : "selection" ,
} ;
} else if ( name === "m2o" ) {
return {
relation : null ,
type : "many2one" ,
} ;
}
throw Exception ( "Should not happen" ) ;
} ,
2012-06-21 23:56:41 +00:00
} ) ;
2011-08-18 17:45:08 +00:00
2012-06-21 23:56:41 +00:00
instance . web . form . FieldBinary = instance . web . form . AbstractField . extend ( instance . web . form . ReinitializeFieldMixin , {
2012-04-18 12:45:20 +00:00
init : function ( field _manager , node ) {
2012-06-13 10:05:16 +00:00
var self = this ;
2012-04-18 12:45:20 +00:00
this . _super ( field _manager , node ) ;
2011-05-19 12:58:45 +00:00
this . binary _value = false ;
2012-06-13 10:05:16 +00:00
this . fileupload _id = _ . uniqueId ( 'oe_fileupload' ) ;
$ ( window ) . on ( this . fileupload _id , function ( ) {
var args = [ ] . slice . call ( arguments ) . slice ( 1 ) ;
self . on _file _uploaded . apply ( self , args ) ;
} ) ;
} ,
stop : function ( ) {
$ ( window ) . off ( this . fileupload _id ) ;
this . _super . apply ( this , arguments ) ;
2011-05-12 15:22:48 +00:00
} ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'input.oe_form_binary_file' ) . change ( this . on _file _change ) ;
this . $el . find ( 'button.oe_form_binary_file_save' ) . click ( this . on _save _as ) ;
this . $el . find ( '.oe_form_binary_file_clear' ) . click ( this . on _clear ) ;
2011-05-18 15:33:56 +00:00
} ,
2011-05-19 12:58:45 +00:00
human _filesize : function ( size ) {
var units = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ] ;
var i = 0 ;
while ( size >= 1024 ) {
size /= 1024 ;
++ i ;
}
return size . toFixed ( 2 ) + ' ' + units [ i ] ;
2011-05-19 10:17:26 +00:00
} ,
2011-05-23 13:38:40 +00:00
on _file _change : function ( e ) {
2011-05-19 10:17:26 +00:00
// TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper
// http://www.html5rocks.com/tutorials/file/dndfiles/
// http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
2012-06-13 10:05:16 +00:00
if ( $ ( e . target ) . val ( ) !== '' ) {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'form.oe_form_binary_form input[name=session_id]' ) . val ( this . session . session _id ) ;
this . $el . find ( 'form.oe_form_binary_form' ) . submit ( ) ;
this . $el . find ( '.oe_form_binary_progress' ) . show ( ) ;
this . $el . find ( '.oe_form_binary' ) . hide ( ) ;
2011-05-23 13:38:40 +00:00
}
2011-05-18 22:36:16 +00:00
} ,
2011-09-06 11:49:21 +00:00
on _file _uploaded : function ( size , name , content _type , file _base64 ) {
2011-05-18 22:36:16 +00:00
if ( size === false ) {
2011-10-13 10:26:53 +00:00
this . do _warn ( "File Upload" , "There was a problem while uploading your file" ) ;
2011-08-11 02:42:46 +00:00
// TODO: use openerp web crashmanager
2011-09-12 13:56:03 +00:00
console . warn ( "Error while uploading file : " , name ) ;
2011-05-18 22:36:16 +00:00
} else {
2012-06-14 15:00:10 +00:00
this . filename = name ;
2011-05-19 12:58:45 +00:00
this . on _file _uploaded _and _valid . apply ( this , arguments ) ;
2011-05-18 22:36:16 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . find ( '.oe_form_binary_progress' ) . hide ( ) ;
this . $el . find ( '.oe_form_binary' ) . show ( ) ;
2011-05-18 22:36:16 +00:00
} ,
2011-09-06 11:49:21 +00:00
on _file _uploaded _and _valid : function ( size , name , content _type , file _base64 ) {
2011-05-19 12:58:45 +00:00
} ,
2012-06-14 15:00:10 +00:00
on _save _as : function ( ev ) {
var value = this . get ( 'value' ) ;
if ( ! value ) {
this . do _warn ( _t ( "Save As..." ) , _t ( "The field is empty, there's nothing to save !" ) ) ;
ev . stopPropagation ( ) ;
} else if ( this . _dirty _flag ) {
2012-06-27 14:12:49 +00:00
var link = this . $ ( '.oe_form_binary_file_save_data' ) [ 0 ] ;
2012-06-14 15:00:10 +00:00
link . download = this . filename || "download.bin" ; // Works on only on Google Chrome
//link.target = '_blank';
link . href = "data:application/octet-stream;base64," + value ;
} else {
2012-07-24 14:12:20 +00:00
instance . web . blockUI ( ) ;
2012-06-14 15:00:10 +00:00
this . session . get _file ( {
url : '/web/binary/saveas_ajax' ,
data : { data : JSON . stringify ( {
model : this . view . dataset . model ,
id : ( this . view . datarecord . id || '' ) ,
field : this . name ,
filename _field : ( this . node . attrs . filename || '' ) ,
context : this . view . dataset . get _context ( )
} ) } ,
2012-07-24 14:12:20 +00:00
complete : instance . web . unblockUI ,
2012-06-14 15:00:10 +00:00
error : instance . webclient . crashmanager . on _rpc _error
} ) ;
ev . stopPropagation ( ) ;
return false ;
}
2011-05-23 13:38:40 +00:00
} ,
2012-04-05 11:26:19 +00:00
set _filename : function ( value ) {
var filename = this . node . attrs . filename ;
if ( this . view . fields [ filename ] ) {
this . view . fields [ filename ] . set _value ( value ) ;
this . view . fields [ filename ] . on _ui _change ( ) ;
}
} ,
2011-05-18 15:33:56 +00:00
on _clear : function ( ) {
2012-04-17 14:32:27 +00:00
if ( this . get ( 'value' ) !== false ) {
2011-05-19 12:58:45 +00:00
this . binary _value = false ;
2012-04-17 15:45:05 +00:00
this . set ( { 'value' : false } ) ;
2011-05-18 15:33:56 +00:00
}
2011-05-19 10:17:26 +00:00
return false ;
2011-05-19 12:58:45 +00:00
}
2012-06-21 23:56:41 +00:00
} ) ;
2011-05-19 12:58:45 +00:00
2012-04-17 12:09:49 +00:00
instance . web . form . FieldBinaryFile = instance . web . form . FieldBinary . extend ( {
2012-03-15 16:21:07 +00:00
template : 'FieldBinaryFile' ,
2012-03-20 10:17:47 +00:00
initialize _content : function ( ) {
2012-03-19 16:18:56 +00:00
this . _super ( ) ;
2012-03-19 16:13:58 +00:00
if ( this . get ( "effective_readonly" ) ) {
var self = this ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'a' ) . click ( function ( ) {
2012-04-17 14:32:27 +00:00
if ( self . get ( 'value' ) ) {
2012-03-19 16:13:58 +00:00
self . on _save _as ( ) ;
}
return false ;
} ) ;
}
2011-10-10 13:33:52 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2011-05-19 12:58:45 +00:00
this . _super . apply ( this , arguments ) ;
2012-03-19 16:13:58 +00:00
this . render _value ( ) ;
} ,
render _value : function ( ) {
if ( ! this . get ( "effective_readonly" ) ) {
var show _value ;
if ( this . node . attrs . filename ) {
show _value = this . view . datarecord [ this . node . attrs . filename ] || '' ;
} else {
2012-04-17 14:32:27 +00:00
show _value = ( this . get ( 'value' ) != null && this . get ( 'value' ) !== false ) ? this . get ( 'value' ) : '' ;
2012-03-19 16:13:58 +00:00
}
2012-08-24 18:27:07 +00:00
this . $el . find ( 'input' ) . eq ( 0 ) . val ( show _value ) ;
2011-12-15 12:04:15 +00:00
} else {
2012-08-24 18:27:07 +00:00
this . $el . find ( 'a' ) . show ( ! ! this . get ( 'value' ) ) ;
2012-04-17 14:32:27 +00:00
if ( this . get ( 'value' ) ) {
2012-03-19 16:13:58 +00:00
var show _value = _t ( "Download" ) + " " + ( this . view . datarecord [ this . node . attrs . filename ] || '' ) ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'a' ) . text ( show _value ) ;
2012-03-19 16:13:58 +00:00
}
2011-12-15 12:04:15 +00:00
}
2011-05-19 12:58:45 +00:00
} ,
2011-09-06 11:49:21 +00:00
on _file _uploaded _and _valid : function ( size , name , content _type , file _base64 ) {
2011-05-19 12:58:45 +00:00
this . binary _value = true ;
2012-04-17 15:45:05 +00:00
this . set ( { 'value' : file _base64 } ) ;
2011-12-15 12:04:15 +00:00
var show _value = name + " (" + this . human _filesize ( size ) + ")" ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'input' ) . eq ( 0 ) . val ( show _value ) ;
2011-05-19 12:58:45 +00:00
this . set _filename ( name ) ;
} ,
2012-04-17 14:32:27 +00:00
set _filename : function ( value _ ) {
2011-05-19 12:58:45 +00:00
var filename = this . node . attrs . filename ;
if ( this . view . fields [ filename ] ) {
2012-04-17 15:45:05 +00:00
this . view . fields [ filename ] . set ( { value : value _ } ) ;
2011-05-19 12:58:45 +00:00
}
} ,
on _clear : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-08-24 18:27:07 +00:00
this . $el . find ( 'input' ) . eq ( 0 ) . val ( '' ) ;
2011-05-19 12:58:45 +00:00
this . set _filename ( '' ) ;
}
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldBinaryImage = instance . web . form . FieldBinary . extend ( {
2012-03-15 16:21:07 +00:00
template : 'FieldBinaryImage' ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2011-10-10 13:33:52 +00:00
this . _super . apply ( this , arguments ) ;
2012-03-19 16:13:58 +00:00
this . render _value ( ) ;
2011-10-10 13:33:52 +00:00
} ,
2012-03-19 16:13:58 +00:00
render _value : function ( ) {
2012-08-21 16:15:17 +00:00
var self = this ;
2012-03-08 14:06:36 +00:00
var url ;
2012-08-08 09:15:32 +00:00
if ( this . get ( 'value' ) && ! /^\d+(\.\d*)? \w+$/ . test ( this . get ( 'value' ) ) ) {
2012-04-17 14:32:27 +00:00
url = 'data:image/png;base64,' + this . get ( 'value' ) ;
} else if ( this . get ( 'value' ) ) {
2012-08-09 13:48:51 +00:00
var id = escape ( JSON . stringify ( this . view . datarecord . id || null ) ) ;
2012-08-21 16:15:17 +00:00
var field = this . name ;
if ( this . options . preview _image )
field = this . options . preview _image ;
2012-03-08 14:06:36 +00:00
url = '/web/binary/image?session_id=' + this . session . session _id + '&model=' +
2012-08-21 16:15:17 +00:00
this . view . dataset . model + '&id=' + id + '&field=' + field + '&t=' + ( new Date ( ) . getTime ( ) ) ;
2012-03-23 15:04:48 +00:00
} else {
url = "/web/static/src/img/placeholder.png" ;
2012-03-08 14:06:36 +00:00
}
2012-08-21 16:15:17 +00:00
var $img = $ ( QWeb . render ( "FieldBinaryImage-img" , { widget : this , url : url } ) ) ;
2012-08-24 18:27:07 +00:00
this . $el . find ( '> img' ) . remove ( ) ;
this . $el . prepend ( $img ) ;
2012-08-21 16:15:17 +00:00
$img . load ( function ( ) {
if ( ! self . options . size )
return ;
$img . css ( "max-width" , "" + self . options . size [ 0 ] + "px" ) ;
$img . css ( "max-height" , "" + self . options . size [ 1 ] + "px" ) ;
$img . css ( "margin-left" , "" + ( self . options . size [ 0 ] - $img . width ( ) ) / 2 + "px" ) ;
$img . css ( "margin-top" , "" + ( self . options . size [ 1 ] - $img . height ( ) ) / 2 + "px" ) ;
} ) ;
2011-05-19 12:58:45 +00:00
} ,
on _file _change : function ( ) {
2012-03-23 15:04:48 +00:00
this . render _value ( ) ;
2011-05-19 12:58:45 +00:00
this . _super . apply ( this , arguments ) ;
} ,
2011-09-06 11:49:21 +00:00
on _file _uploaded _and _valid : function ( size , name , content _type , file _base64 ) {
2012-04-17 14:32:27 +00:00
this . set ( { 'value' : file _base64 } ) ;
2011-05-19 12:58:45 +00:00
this . binary _value = true ;
2012-03-23 15:04:48 +00:00
this . render _value ( ) ;
2011-05-19 12:58:45 +00:00
} ,
on _clear : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-03-23 15:04:48 +00:00
this . render _value ( ) ;
2011-05-12 15:22:48 +00:00
}
} ) ;
2012-04-17 12:09:49 +00:00
instance . web . form . FieldStatus = instance . web . form . AbstractField . extend ( {
2012-07-02 08:41:19 +00:00
template : "FieldStatus" ,
2012-08-18 22:51:51 +00:00
init : function ( field _manager , node ) {
this . _super ( field _manager , node ) ;
this . options . clickable = this . options . clickable || ( this . node . attrs || { } ) . clickable || false ;
this . options . visible = this . options . visible || ( this . node . attrs || { } ) . statusbar _visible || false ;
this . selected _value = null ;
} ,
2011-09-08 14:58:45 +00:00
start : function ( ) {
2011-09-08 15:06:45 +00:00
this . _super ( ) ;
2012-08-18 22:10:35 +00:00
// backward compatibility
this . loaded = new $ . Deferred ( ) ;
if ( this . options . clickable ) {
2012-08-24 18:27:07 +00:00
this . $el . on ( 'click' , 'li' , this . on _click _stage ) ;
2012-08-18 22:10:35 +00:00
}
// TODO move the following into css :after
2012-08-24 18:27:07 +00:00
if ( this . $el . parent ( ) . is ( 'header' ) ) {
this . $el . after ( '<div class="oe_clear"/>' ) ;
2012-07-02 08:41:19 +00:00
}
2011-09-08 14:58:45 +00:00
} ,
2012-04-17 14:32:27 +00:00
set _value : function ( value _ ) {
2012-05-25 10:10:57 +00:00
var self = this ;
2012-04-17 14:32:27 +00:00
this . _super ( value _ ) ;
2012-05-31 16:06:12 +00:00
// find selected value:
// - many2one: [2, "New"] -> 2
// - selection: new -> new
2012-05-18 12:23:48 +00:00
if ( this . field . type == "many2one" ) {
this . selected _value = value _ [ 0 ] ;
2012-05-31 16:06:12 +00:00
} else {
2012-05-18 12:23:48 +00:00
this . selected _value = value _ ;
}
2012-05-25 10:10:57 +00:00
// trick to be sure all values are loaded in the form, therefore
// enabling the evaluation of dynamic domains
2012-08-18 22:10:35 +00:00
self . selection = [ ] ;
2012-05-25 10:10:57 +00:00
$ . async _when ( ) . then ( function ( ) {
2012-08-18 22:10:35 +00:00
self . get _selection ( ) ;
2012-05-25 10:10:57 +00:00
} ) ;
2012-08-18 22:10:35 +00:00
return this . loaded ;
2011-09-08 14:58:45 +00:00
} ,
2012-08-18 22:10:35 +00:00
/ * * G e t t h e s e l e c t i o n a n d r e n d e r i t
* selection : [ [ identifier , value _to _display ] , ... ]
2012-05-25 10:10:57 +00:00
* For selection fields : this is directly given by this . field . selection
2012-08-18 22:10:35 +00:00
* For many2one fields : perform a search on the relation of the many2one field
2012-05-25 10:10:57 +00:00
* /
2012-05-18 11:47:31 +00:00
get _selection : function ( ) {
2011-09-08 16:35:36 +00:00
var self = this ;
2012-05-18 11:47:31 +00:00
if ( this . field . type == "many2one" ) {
2012-08-18 22:10:35 +00:00
var domain = new instance . web . CompoundDomain ( [ '|' ] , self . build _domain ( ) , [ [ 'id' , '=' , self . selected _value ] ] ) ;
var ds = new instance . web . DataSetSearch ( this , this . field . relation , self . build _context ( ) , domain ) ;
ds . read _slice ( [ 'name' ] , { } ) . done ( function ( records ) {
for ( var i = 0 ; i < records . length ; i ++ ) {
self . selection . push ( [ records [ i ] . id , records [ i ] . name ] ) ;
}
self . render _elements ( ) ;
self . loaded . resolve ( ) ;
2012-05-18 11:47:31 +00:00
} ) ;
2012-05-31 16:06:12 +00:00
} else {
2012-08-18 22:10:35 +00:00
this . loaded . resolve ( ) ;
// For field type selection filter values according to
// statusbar_visible attribute of the field. For example:
// statusbar_visible="draft,open".
var selection = this . field . selection ;
for ( var i = 0 ; i < selection . length ; i ++ ) {
var key = selection [ i ] [ 0 ] ;
if ( key == this . selected _value || ! this . options . visible || this . options . visible . indexOf ( key ) != - 1 ) {
this . selection . push ( selection [ i ] ) ;
}
}
this . render _elements ( ) ;
2011-09-08 16:35:36 +00:00
}
2012-05-18 12:22:32 +00:00
} ,
2012-05-30 07:52:52 +00:00
/ * * R e n d e r s t h e w i d g e t . T h i s f u n c t i o n a l s o c h e c k s f o r s t a t u s b a r _ c o l o r s = ' { " p e n d i n g " : " b l u e " } '
2012-05-25 10:10:57 +00:00
* attribute in the widget . This allows to set a given color to a given
2012-05-31 16:06:12 +00:00
* state ( given by the key of ( key , label ) ) .
* /
2012-05-18 11:47:31 +00:00
render _elements : function ( ) {
2012-07-19 09:14:46 +00:00
var self = this ;
2012-08-18 22:10:35 +00:00
var content = instance . web . qweb . render ( "FieldStatus.content" , { widget : this } ) ;
2012-08-24 18:27:07 +00:00
this . $el . html ( content ) ;
2011-09-09 14:34:50 +00:00
var colors = JSON . parse ( ( this . node . attrs || { } ) . statusbar _colors || "{}" ) ;
2011-09-08 17:00:12 +00:00
var color = colors [ this . selected _value ] ;
2012-08-18 20:16:32 +00:00
if ( color ) {
2012-08-18 22:10:35 +00:00
this . $ ( "oe_active" ) . css ( "color" , color ) ;
2011-10-17 12:09:01 +00:00
}
2012-05-18 14:41:42 +00:00
} ,
2012-08-18 22:10:35 +00:00
on _click _stage : function ( ev ) {
var self = this ;
var $li = $ ( ev . currentTarget ) ;
var val = parseInt ( $li . data ( "id" ) ) ;
if ( val != self . selected _value ) {
this . view . recursive _save ( ) . then ( function ( ) {
var change = { } ;
change [ self . name ] = val ;
self . view . dataset . write ( self . view . datarecord . id , change ) . then ( function ( ) {
self . view . reload ( ) ;
} ) ;
} ) ;
}
2012-08-18 20:16:32 +00:00
} ,
2011-09-08 14:58:45 +00:00
} ) ;
2011-03-31 10:57:51 +00:00
/ * *
2012-05-02 09:27:43 +00:00
* Registry of form fields , called by : js : ` instance.web.FormView ` .
*
2012-06-21 23:56:41 +00:00
* All referenced classes must implement FieldInterface . Those represent the classes whose instances
2012-05-02 09:27:43 +00:00
* will substitute to the < field > tags as defined in OpenERP ' s views .
2011-03-31 10:57:51 +00:00
* /
2012-04-17 12:09:49 +00:00
instance . web . form . widgets = new instance . web . Registry ( {
'char' : 'instance.web.form.FieldChar' ,
'id' : 'instance.web.form.FieldID' ,
'email' : 'instance.web.form.FieldEmail' ,
'url' : 'instance.web.form.FieldUrl' ,
'text' : 'instance.web.form.FieldText' ,
2012-08-13 11:46:38 +00:00
'html' : 'instance.web.form.FieldTextHtml' ,
2012-04-17 12:09:49 +00:00
'date' : 'instance.web.form.FieldDate' ,
'datetime' : 'instance.web.form.FieldDatetime' ,
'selection' : 'instance.web.form.FieldSelection' ,
'many2one' : 'instance.web.form.FieldMany2One' ,
2012-05-04 11:06:15 +00:00
'many2many' : 'instance.web.form.FieldMany2Many' ,
2012-05-25 15:05:44 +00:00
'many2many_tags' : 'instance.web.form.FieldMany2ManyTags' ,
'many2many_kanban' : 'instance.web.form.FieldMany2ManyKanban' ,
2012-04-17 12:09:49 +00:00
'one2many' : 'instance.web.form.FieldOne2Many' ,
'one2many_list' : 'instance.web.form.FieldOne2Many' ,
'reference' : 'instance.web.form.FieldReference' ,
'boolean' : 'instance.web.form.FieldBoolean' ,
'float' : 'instance.web.form.FieldFloat' ,
'integer' : 'instance.web.form.FieldFloat' ,
'float_time' : 'instance.web.form.FieldFloat' ,
'progressbar' : 'instance.web.form.FieldProgressBar' ,
'image' : 'instance.web.form.FieldBinaryImage' ,
'binary' : 'instance.web.form.FieldBinaryFile' ,
'statusbar' : 'instance.web.form.FieldStatus'
2011-03-31 10:57:51 +00:00
} ) ;
2011-10-31 10:27:52 +00:00
2012-05-02 09:27:43 +00:00
/ * *
* Registry of widgets usable in the form view that can substitute to any possible
* tags defined in OpenERP ' s form views .
*
* Every referenced class should extend FormWidget .
* /
2012-04-17 12:09:49 +00:00
instance . web . form . tags = new instance . web . Registry ( {
'button' : 'instance.web.form.WidgetButton' ,
2012-04-10 13:04:44 +00:00
} ) ;
2011-03-30 14:00:48 +00:00
} ;
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: