diff options
| author | Vidhin Mehta <vme@tinyerp.com> | 2013-05-23 10:44:07 +0530 |
|---|---|---|
| committer | Vidhin Mehta <vme@tinyerp.com> | 2013-05-23 10:44:07 +0530 |
| commit | e9fadb6ff8c899ad3820f16abd96c8e40fa4cd91 (patch) | |
| tree | 556870c837e984f68d3ec7f497d3d7558a29a752 /addons/web/static/src | |
| parent | b585ca8f093bbb479d0767eafc3bbb7602a87123 (diff) | |
| parent | 978c76c5ea6bf16f7db2f94630ceb4b27caec037 (diff) | |
[MERGE]Trunk.
bzr revid: vme@tinyerp.com-20130514120256-0xob1bgm77zrsp27
bzr revid: vme@tinyerp.com-20130523051407-vyl5oy2ubm1on2r3
Diffstat (limited to 'addons/web/static/src')
| -rw-r--r-- | addons/web/static/src/css/base.css | 138 | ||||
| -rw-r--r-- | addons/web/static/src/css/base.sass | 82 | ||||
| -rw-r--r-- | addons/web/static/src/fixbind.js | 28 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome.js | 19 | ||||
| -rw-r--r-- | addons/web/static/src/js/data.js | 48 | ||||
| -rw-r--r-- | addons/web/static/src/js/formats.js | 30 | ||||
| -rw-r--r-- | addons/web/static/src/js/search.js | 38 | ||||
| -rw-r--r-- | addons/web/static/src/js/testing.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_form.js | 147 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list.js | 7 | ||||
| -rw-r--r-- | addons/web/static/src/js/views.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 21 |
12 files changed, 394 insertions, 171 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 2dab643e931..b61c599ba62 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -544,6 +544,29 @@ background: #7c7bad; color: #eeeeee; } +.openerp .oe_form_field_radio.oe_horizontal { + white-space: nowrap; +} +.openerp .oe_form_field_radio.oe_horizontal label { + display: inline-block; + text-align: center; + height: 16px; +} +.openerp .oe_form_field_radio.oe_vertical label { + margin-left: 4px; +} +.openerp .oe_form_field_radio.oe_form_required .oe_radio_input { + border: 2px solid transparent; + display: inline-block; + height: 12px; + width: 12px; + vertical-align: bottom; + border-radius: 10px; + margin: 1px 0; +} +.openerp .oe_form_field_radio.oe_form_required.oe_form_invalid .oe_radio_input { + border-color: red; +} .openerp .oe_tags { margin-bottom: 1px; } @@ -1269,7 +1292,7 @@ color: white; padding: 2px 4px; margin: 1px 6px 0 0; - border: 1px solid lightGray; + border: 1px solid lightgrey; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1301,7 +1324,7 @@ transform: scale(1.1); } .openerp .oe_secondary_submenu .oe_active { - border-top: 1px solid lightGray; + border-top: 1px solid lightgrey; border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); @@ -1706,6 +1729,7 @@ font-size: 1px; letter-spacing: -1px; color: transparent; + text-shadow: none; font-weight: normal; -moz-box-shadow: none; -webkit-box-shadow: none; @@ -1841,12 +1865,12 @@ margin: 0; } .openerp .oe_searchview .oe_searchview_drawer h3 { - margin: 8px 4px 4px 12px; + margin: 8px 4px 4px 0px; color: #7c7bad; font-size: 13px; } .openerp .oe_searchview .oe_searchview_drawer h4, .openerp .oe_searchview .oe_searchview_drawer h4 * { - margin: 0; + margin: 0 0 0 2px; cursor: pointer; font-weight: normal; display: inline-block; @@ -1923,6 +1947,12 @@ .openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom { padding: 0 8px 8px 8px; } +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom div { + padding: 0; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom div h4 { + margin: 0; +} .openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form { display: none; } @@ -2164,6 +2194,7 @@ } .openerp .oe_form header { position: relative; + z-index: 1; border-bottom: 1px solid #cacaca; padding-left: 2px; background-color: #ededed; @@ -2289,7 +2320,7 @@ } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; - color: darkGreen; + color: darkgreen; vertical-align: top; position: relative; top: -4px; @@ -2315,30 +2346,6 @@ display: inline-block; min-width: 70px; } -.openerp .oe_form_field_radio.oe_horizontal { - white-space: nowrap; -} -.openerp .oe_form_field_radio.oe_form_required .oe_radio_input { - border: 1px solid transparent; - display: inline-block; - height: 12px; - width: 12px; - vertical-align: bottom; - border-radius: 10px; - margin: 1px 0; -} -.openerp .oe_form_field_radio.oe_form_required.oe_form_invalid .oe_radio_input { - border-color: red; -} -.openerp .oe_form_field_radio.oe_horizontal label, -.openerp .oe_form_field_radio.oe_horizontal div { - display: inline-block; - text-align: center; - height: 16px; -} -.openerp .oe_form_field_radio.oe_vertical label { - margin-left: 4px; -} .openerp .oe_form_field_progressbar.ui-progressbar { height: 22px; font-size: 10px; @@ -2499,10 +2506,10 @@ width: 7em; } .openerp .oe_form_editable .oe_form .oe_form_field_date input { - width: 7.5em; + width: 100px; } .openerp .oe_form_editable .oe_form .oe_form_field_datetime input { - width: 11.5em; + width: 150px; } .openerp .oe_hidden_input_file { position: relative; @@ -2662,20 +2669,20 @@ background-image: -o-linear-gradient(top, #fcfcfc, #dedede); background-image: linear-gradient(to bottom, #fcfcfc, #dedede); } -.openerp ul.oe_form_status li:first-child .label, .openerp ul.oe_form_status_clickable li:first-child .label { +.openerp ul.oe_form_status li:first-child > .label, .openerp ul.oe_form_status_clickable li:first-child > .label { border-left: 1px solid #cacaca; padding-left: 14px; } .openerp ul.oe_form_status li:last-child, .openerp ul.oe_form_status_clickable li:last-child { border-right: 1px solid #cacaca; } -.openerp ul.oe_form_status li:last-child .label, .openerp ul.oe_form_status_clickable li:last-child .label { +.openerp ul.oe_form_status li:last-child > .label, .openerp ul.oe_form_status_clickable li:last-child > .label { padding-right: 14px; } -.openerp ul.oe_form_status li:last-child .arrow, .openerp ul.oe_form_status_clickable li:last-child .arrow { +.openerp ul.oe_form_status li:last-child > .arrow, .openerp ul.oe_form_status_clickable li:last-child > .arrow { display: none; } -.openerp ul.oe_form_status li .label, .openerp ul.oe_form_status_clickable li .label { +.openerp ul.oe_form_status li > .label, .openerp ul.oe_form_status_clickable li > .label { color: #4c4c4c; text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede; padding: 7px; @@ -2684,14 +2691,14 @@ margin: 0; position: relative; } -.openerp ul.oe_form_status li .arrow, .openerp ul.oe_form_status_clickable li .arrow { +.openerp ul.oe_form_status li > .arrow, .openerp ul.oe_form_status_clickable li > .arrow { width: 17px; display: inline-block; vertical-align: top; overflow: hidden; margin-left: -5px; } -.openerp ul.oe_form_status li .arrow span, .openerp ul.oe_form_status_clickable li .arrow span { +.openerp ul.oe_form_status li > .arrow span, .openerp ul.oe_form_status_clickable li > .arrow span { position: relative; width: 24px; height: 24px; @@ -2713,6 +2720,32 @@ -o-transform: rotate(45deg); transform: rotate(45deg); } +.openerp ul.oe_form_status .oe_folded .label, .openerp ul.oe_form_status_clickable .oe_folded .label { + padding-bottom: 8px; +} +.openerp ul.oe_form_status .oe_folded ul, .openerp ul.oe_form_status_clickable .oe_folded ul { + position: absolute; + border-top: 1px solid #cacaca; + float: none; + top: 30px; + right: 0; + display: none; +} +.openerp ul.oe_form_status .oe_folded ul li, .openerp ul.oe_form_status_clickable .oe_folded ul li { + margin: 0; + border-left: 1px solid #cacaca; + display: block; +} +.openerp ul.oe_form_status .oe_folded ul li.label, .openerp ul.oe_form_status_clickable .oe_folded ul li.label { + border: 0; + padding-left: 24px; +} +.openerp ul.oe_form_status .oe_folded ul li:last-child, .openerp ul.oe_form_status_clickable .oe_folded ul li:last-child { + border-bottom: 1px solid #cacaca; +} +.openerp ul.oe_form_status .oe_folded:hover ul, .openerp ul.oe_form_status_clickable .oe_folded:hover ul { + display: block; +} .openerp ul.oe_form_status li.oe_active, .openerp ul.oe_form_status_clickable li.oe_active { background-color: #5382b9; background-image: -webkit-gradient(linear, left top, left bottom, from(#729fcf), to(#3465a4)); @@ -2722,14 +2755,14 @@ background-image: -o-linear-gradient(top, #729fcf, #3465a4); background-image: linear-gradient(to bottom, #729fcf, #3465a4); } -.openerp ul.oe_form_status li.oe_active .arrow span, .openerp ul.oe_form_status_clickable li.oe_active .arrow span { +.openerp ul.oe_form_status li.oe_active > .arrow span, .openerp ul.oe_form_status_clickable li.oe_active > .arrow span { background-color: #3465a4; background: -moz-linear-gradient(135deg, #3465a4, #729fcf); background: -o-linear-gradient(135deg, #729fcf, #3465a4); background: -webkit-gradient(linear, left top, right bottom, from(#729fcf), to(#3465a4)); background: -ms-linear-gradient(top, #729fcf, #3465a4); } -.openerp ul.oe_form_status li.oe_active .label, .openerp ul.oe_form_status_clickable li.oe_active .label { +.openerp ul.oe_form_status li.oe_active > .label, .openerp ul.oe_form_status_clickable li.oe_active > .label { color: white; text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4; } @@ -2745,10 +2778,10 @@ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca); background-image: linear-gradient(to bottom, #e8e8e8, #cacaca); } -.openerp ul.oe_form_status_clickable li:hover .label { +.openerp ul.oe_form_status_clickable li:hover > .label { text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede; } -.openerp ul.oe_form_status_clickable li:hover .arrow span { +.openerp ul.oe_form_status_clickable li:hover > .arrow span { background-color: #d9d9d9; background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#cacaca)); background-image: -webkit-linear-gradient(top, #e8e8e8, #cacaca); @@ -2757,7 +2790,7 @@ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca); background-image: linear-gradient(to bottom, #e8e8e8, #cacaca); } -.openerp ul.oe_form_status_clickable li .label { +.openerp ul.oe_form_status_clickable li > .label { color: #7c7bad; } .openerp ul.oe_form_status_clickable li.oe_active:hover { @@ -2769,10 +2802,10 @@ background-image: -o-linear-gradient(top, #4c85c2, #284d7d); background-image: linear-gradient(to bottom, #4c85c2, #284d7d); } -.openerp ul.oe_form_status_clickable li.oe_active:hover .label { +.openerp ul.oe_form_status_clickable li.oe_active:hover > .label { text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4; } -.openerp ul.oe_form_status_clickable li.oe_active:hover .arrow span { +.openerp ul.oe_form_status_clickable li.oe_active:hover > .arrow span { background-color: #284d7d; background: -moz-linear-gradient(135deg, #284d7d, #4c85c2); background: -o-linear-gradient(135deg, #4c85c2, #284d7d); @@ -3049,6 +3082,7 @@ font-size: 1px; letter-spacing: -1px; color: transparent; + text-shadow: none; font-weight: normal; margin-right: 7px; } @@ -3184,6 +3218,7 @@ .kitten-mode-activated > * { opacity: 0.7; } + .loading-kitten { -moz-border-radius: 15px; -webkit-border-radius: 15px; @@ -3246,6 +3281,9 @@ div.ui-widget-overlay { color: #afafb6 !important; font-style: italic !important; } +.openerp_ie .oe_form_binary_file { + width: 80px; +} .openerp_ie .oe_form_field_boolean input { background: white; } @@ -3346,11 +3384,11 @@ div.ui-widget-overlay { overflow: hidden; border-right: 1px solid #cacaca; } -.openerp_ie ul.oe_form_status li:last-child .label, .openerp_ie ul.oe_form_status_clickable li:last-child .label { +.openerp_ie ul.oe_form_status li:last-child > .label, .openerp_ie ul.oe_form_status_clickable li:last-child > .label { padding-right: 14px; border-right: none; } -.openerp_ie ul.oe_form_status li:last-child .arrow, .openerp_ie ul.oe_form_status_clickable li:last-child .arrow { +.openerp_ie ul.oe_form_status li:last-child > .arrow, .openerp_ie ul.oe_form_status_clickable li:last-child > .arrow { display: inline-block; opacity: 0; filter: alpha(opacity=0); @@ -3358,17 +3396,17 @@ div.ui-widget-overlay { width: 0; border-right: none; } -.openerp_ie ul.oe_form_status .label, .openerp_ie ul.oe_form_status_clickable .label { +.openerp_ie ul.oe_form_status li > .label, .openerp_ie ul.oe_form_status_clickable li > .label { border-bottom: 1px solid #cacaca; background: transparent; } -.openerp_ie ul.oe_form_status .arrow span, .openerp_ie ul.oe_form_status_clickable .arrow span { +.openerp_ie ul.oe_form_status li > .arrow span, .openerp_ie ul.oe_form_status_clickable li > .arrow span { background-color: #fefefe !important; } -.openerp_ie ul.oe_form_status li.oe_active .label, .openerp_ie ul.oe_form_status_clickable li.oe_active .label { +.openerp_ie ul.oe_form_status li.oe_active > .label, .openerp_ie ul.oe_form_status_clickable li.oe_active > .label { border-bottom: 1px solid #729fcf; } -.openerp_ie ul.oe_form_status li.oe_active .arrow span, .openerp_ie ul.oe_form_status_clickable li.oe_active .arrow span { +.openerp_ie ul.oe_form_status li.oe_active > .arrow span, .openerp_ie ul.oe_form_status_clickable li.oe_active > .arrow span { background-color: #729fcf !important; } .openerp_ie .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 056293a4559..099b1c871f9 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -123,6 +123,7 @@ $sheet-padding: 16px font-size: 1px letter-spacing: -1px color: transparent + text-shadow: none font-weight: normal &:before font: 21px "mnmliconsRegular" @@ -133,6 +134,7 @@ $sheet-padding: 16px font-size: 1px letter-spacing: -1px color: transparent + text-shadow: none font-weight: normal &:before font: $size "entypoRegular" @@ -1483,11 +1485,11 @@ $sheet-padding: 16px border-top: none margin: 0 h3 - margin: 8px 4px 4px 12px + margin: 8px 4px 4px 0px color: $section-title-color font-size: 13px h4, h4 * - margin: 0 + margin: 0 0 0 2px cursor: pointer font-weight: normal display: inline-block @@ -1543,6 +1545,10 @@ $sheet-padding: 16px margin: 0 0 8px 0 .oe_searchview_custom padding: 0 8px 8px 8px + div + padding: 0 + h4 + margin: 0 form display: none li @@ -1733,6 +1739,7 @@ $sheet-padding: 16px // FormView.header {{{ .oe_form header position: relative + z-index: 1 border-bottom: 1px solid #cacaca padding-left: 2px @include vertical-gradient(#fcfcfc, #dedede) @@ -1981,9 +1988,9 @@ $sheet-padding: 16px .oe_form_field_float input width: 7em .oe_form_field_date input - width: 7.5em + width: 100px .oe_form_field_datetime input - width: 11.5em + width: 150px // }}} // FormView.fields_binary {{{ /* http://www.quirksmode.org/dom/inputfile.html @@ -2120,16 +2127,16 @@ $sheet-padding: 16px margin: 0 -18px 0 0 padding: 0 @include vertical-gradient(#fcfcfc, #dedede) - &:first-child .label + &:first-child > .label border-left: 1px solid #cacaca padding-left: 14px &:last-child border-right: 1px solid #cacaca - .label + > .label padding-right: 14px - .arrow + > .arrow display: none - .label + > .label color: #4c4c4c text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede padding: 7px @@ -2137,7 +2144,7 @@ $sheet-padding: 16px padding-left: 24px margin: 0 position: relative - .arrow + > .arrow width: 17px display: inline-block vertical-align: top @@ -2154,11 +2161,32 @@ $sheet-padding: 16px @include skew-gradient(#fcfcfc, #dedede) @include radius(3px) @include transform(rotate(45deg)) + .oe_folded + .label + padding-bottom: 8px + ul + position: absolute + border-top: 1px solid #cacaca + float: none + top: 30px + right: 0 + display: none + li + margin: 0 + border-left: 1px solid #cacaca + display: block + li.label + border: 0 + padding-left: 24px + li:last-child + border-bottom: 1px solid #cacaca + &:hover ul + display: block li.oe_active @include vertical-gradient(#729fcf, #3465a4) - .arrow span + > .arrow span @include skew-gradient(#729fcf, #3465a4) - .label + > .label color: white text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4 ul.oe_form_status_clickable @@ -2166,18 +2194,18 @@ $sheet-padding: 16px cursor: pointer &:hover @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%)) - .label + > .label text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede - .arrow span + > .arrow span @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%)) - .label + > .label color: $link-color li.oe_active &:hover @include vertical-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) - .label + > .label text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4 - .arrow span + > .arrow span @include skew-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) // }}} // FormView.one2many {{{ @@ -2560,6 +2588,8 @@ div.ui-widget-overlay .placeholder color: $tag-border !important font-style: italic !important + .oe_form_binary_file + width: 80px .oe_form_field_boolean input background: #fff .db_option_table .oe_form_field_selection @@ -2640,26 +2670,26 @@ div.ui-widget-overlay &:last-child overflow: hidden border-right: 1px solid #cacaca - .label + > .label padding-right: 14px border-right: none - .arrow + > .arrow display: inline-block opacity: 0 filter: alpha(opacity = 0) border: none width: 0 border-right: none - .label - border-bottom: 1px solid #cacaca - background: transparent - .arrow - span - background-color: #fefefe !important + > .label + border-bottom: 1px solid #cacaca + background: transparent + > .arrow + span + background-color: #fefefe !important li.oe_active - .label + > .label border-bottom: 1px solid #729fcf - .arrow span + > .arrow span background-color: #729fcf !important // jquery ui for ie diff --git a/addons/web/static/src/fixbind.js b/addons/web/static/src/fixbind.js new file mode 100644 index 00000000000..4a441ce0b9e --- /dev/null +++ b/addons/web/static/src/fixbind.js @@ -0,0 +1,28 @@ +// Fix old versions of Webkit (such as ones used on iOS < 6 or PhantomJS <= 1.7) +// which does not have Function.prototype.bind function + +// Use moz polyfill: +// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 3870a7999b4..4a89eee8984 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -251,9 +251,9 @@ instance.web.CrashManager = instance.web.Class.extend({ if (handler) { new (handler)(this, error).display(); return; - }; + } if (error.data.name === "openerp.addons.web.session SessionExpiredException") { - this.show_warning({type: "Session Expired", data: { message: "Your OpenERP session expired. Please refresh the current web page." }}); + this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }}); return; } if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning" @@ -530,16 +530,11 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ 'login': 'admin', 'password': form_obj['create_admin_pwd'], 'login_successful': function() { - var action = { - type: "ir.actions.client", - tag: 'reload', - params: { - url_search : { - db: form_obj['db_name'], - }, - } - }; - self.do_action(action); + var url = '/?db=' + form_obj['db_name']; + if (self.session.debug) { + url += '&debug'; + } + instance.web.redirect(url); }, }, _push_me: false, diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index f1b043bacf9..d60f72c6577 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -112,24 +112,27 @@ instance.web.Query = instance.web.Class.extend({ * @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null} */ group_by: function (grouping) { - if (grouping === undefined) { - return null; + var ctx = instance.web.pyeval.eval( + 'context', this._model.context(this._context)); + + // undefined passed in explicitly (!) + if (_.isUndefined(grouping)) { + grouping = []; } if (!(grouping instanceof Array)) { grouping = _.toArray(arguments); } - if (_.isEmpty(grouping)) { return null; } + if (_.isEmpty(grouping) && !ctx['group_by_no_leaf']) { + return null; + } var self = this; - - var ctx = instance.web.pyeval.eval( - 'context', this._model.context(this._context)); return this._model.call('read_group', { groupby: grouping, fields: _.uniq(grouping.concat(this._fields || [])), domain: this._model.domain(this._filter), - context: this._model.context(this._context), + context: ctx, offset: this._offset, limit: this._limit, orderby: instance.web.serialize_sort(this._order_by) || false @@ -325,7 +328,7 @@ instance.web.Model = instance.web.Class.extend({ * Fetches the model's domain, combined with the provided domain if any * * @param {Array} [domain] to combine with the model's internal domain - * @returns The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain + * @returns {instance.web.CompoundDomain} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain */ domain: function (domain) { if (!domain) { return this._domain; } @@ -337,7 +340,7 @@ instance.web.Model = instance.web.Class.extend({ * combined with the provided context if any * * @param {Object} [context] to combine with the model's internal context - * @returns The union of the user's context and the model's internal context, as well as the provided context if any. In that order. + * @returns {instance.web.CompoundContext} The union of the user's context and the model's internal context, as well as the provided context if any. In that order. */ context: function (context) { return new instance.web.CompoundContext( @@ -604,6 +607,9 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin, alter_ids: function(n_ids) { this.ids = n_ids; }, + remove_ids: function (ids) { + this.alter_ids(_(this.ids).difference(ids)); + }, /** * Resequence records. * @@ -701,22 +707,28 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({ get_domain: function (other_domain) { this._model.domain(other_domain); }, + alter_ids: function (ids) { + this._super(ids); + if (this.index !== null && this.index >= this.ids.length) { + this.index = this.ids.length > 0 ? this.ids.length - 1 : 0; + } + }, + remove_ids: function (ids) { + var before = this.ids.length; + this._super(ids); + if (this._length) { + this._length -= (before - this.ids.length); + } + }, unlink: function(ids, callback, error_callback) { var self = this; return this._super(ids).done(function(result) { - self.ids = _(self.ids).difference(ids); - if (self._length) { - self._length -= 1; - } - if (self.index !== null) { - self.index = self.index <= self.ids.length - 1 ? - self.index : (self.ids.length > 0 ? self.ids.length -1 : 0); - } + self.remove_ids( ids); self.trigger("dataset_changed", ids, callback, error_callback); }); }, size: function () { - if (this._length !== undefined) { + if (this._length != null) { return this._length; } return this._super(); diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 851b60af229..854c5f76218 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -314,4 +314,34 @@ instance.web.auto_date_to_str = function(value, type) { } }; +/** + * performs a half up rounding with arbitrary precision, correcting for float loss of precision + * See the corresponding float_round() in server/tools/float_utils.py for more info + * @param {Number} the value to be rounded + * @param {Number} a non zero precision parameter. eg: 0.01 rounds to two digits. + */ +instance.web.round_precision = function(value, precision){ + if(!value){ + return 0; + }else if(!precision){ + throw new Error('round_precision(...): Cannot round value: '+value+' with a precision of zero (or undefined)'); + } + var normalized_value = value / precision; + var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2); + var epsilon = Math.pow(2, epsilon_magnitude - 53); + normalized_value += normalized_value >= 0 ? epsilon : -epsilon; + var rounded_value = Math.round(normalized_value); + return rounded_value * precision; +}; + +/** + * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision + * See the corresponding float_round() in server/tools/float_utils.py for more info + * @param {Number} the value to be rounded + * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14 + */ +instance.web.round_decimals = function(value, decimals){ + return instance.web.round_precision(value, Math.pow(10,-decimals)); +}; + }; diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index f5449ed8bba..0051598e48a 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -326,7 +326,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea } }, 'autocompleteopen': function () { - this.$el.autocomplete('widget').css('z-index', 3); + this.$el.autocomplete('widget').css('z-index', 1004); }, }, /** @@ -1039,7 +1039,9 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in facet.values.each(function (v) { var i = _(self.filters).indexOf(v.get('value')); if (i === -1) { return; } - $filters.eq(i).addClass('oe_selected'); + $filters.filter(function () { + return Number($(this).data('index')) === i; + }).addClass('oe_selected'); }); }, /** @@ -1129,7 +1131,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in }); }, toggle_filter: function (e) { - this.toggle(this.filters[$(e.target).index()]); + this.toggle(this.filters[Number($(e.target).data('index'))]); }, toggle: function (filter) { this.view.query.toggle(this.make_facet([this.make_value(filter)])); @@ -1337,20 +1339,22 @@ instance.web.search.CharField = instance.web.search.Field.extend( /** @lends ins } }); instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{ - value_from: function () { - if (!this.$el.val()) { - return null; - } - var val = this.parse(this.$el.val()), - check = Number(this.$el.val()); - if (isNaN(val) || val !== check) { - this.$el.addClass('error'); - throw new instance.web.search.Invalid( - this.attrs.name, this.$el.val(), this.error_message); - } - this.$el.removeClass('error'); - return val; - } + complete: function (value) { + var val = this.parse(value); + if (isNaN(val)) { return $.when(); } + var label = _.str.sprintf( + _t("Search %(field)s for: %(value)s"), { + field: '<em>' + this.attrs.string + '</em>', + value: '<strong>' + _.str.escapeHTML(value) + '</strong>'}); + return $.when([{ + label: label, + facet: { + category: this.attrs.string, + field: this, + values: [{label: value, value: val}] + } + }]); + }, }); /** * @class diff --git a/addons/web/static/src/js/testing.js b/addons/web/static/src/js/testing.js index e7aad43ffc7..e665a43db01 100644 --- a/addons/web/static/src/js/testing.js +++ b/addons/web/static/src/js/testing.js @@ -176,9 +176,9 @@ openerp.testing = {}; }); QUnit.module(testing.current_module + '.' + name, {_oe: options}); - body(testing.case); + body(testing['case']); }; - testing.case = function (name, options, callback) { + testing['case'] = function (name, options, callback) { if (_.isFunction(options)) { callback = options; options = {}; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 53a7425c036..454f20ddcd5 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -765,7 +765,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.has_been_loaded.done(function() { if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) { self.dataset.unlink([self.datarecord.id]).done(function() { - self.execute_pager_action('next'); + if (self.dataset.size()) { + self.execute_pager_action('next'); + } else { + self.do_action('history_back'); + } def.resolve(); }); } else { @@ -802,6 +806,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (save_obj.error) return $.Deferred().reject(); return $.when.apply($, save_obj.ret); + }).done(function() { + self.$el.removeClass('oe_form_dirty'); }); }, _process_save: function(save_obj) { @@ -1021,7 +1027,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return value; } var fields = _.chain(this.fields) - .map(function (field, name) { + .map(function (field) { var value = field.get_value(); // ignore fields which are empty, invisible, readonly, o2m // or m2m @@ -1036,7 +1042,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } return { - name: name, + name: field.name, string: field.string, value: value, displayed: display(field, value), @@ -1047,10 +1053,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM .value(); var conditions = _.chain(self.fields) .filter(function (field) { return field.field.change_default; }) - .map(function (field, name) { + .map(function (field) { var value = field.get_value(); return { - name: name, + name: field.name, string: field.string, value: value, displayed: display(field, value), @@ -1444,6 +1450,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt $(this).children().each(function() { var $td = $(this), $child = $td.children(':first'); + if ($child.attr('cell-class')) { + $td.addClass($child.attr('cell-class')); + } switch ($child[0].tagName.toLowerCase()) { case 'separator': break; @@ -1520,7 +1529,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt if (! page.__ic) return; page.__ic.on("change:effective_invisible", null, function() { - if (!page.__ic.get('effective_invisible')) { + if (!page.__ic.get('effective_invisible') && page.autofocus) { $new_notebook.tabs('select', i); return; } @@ -2126,7 +2135,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w value without triggering a re-rendering. */ internal_set_value: function(value_) { - var tmp = this.no_render; + var tmp = this.no_rerender; this.no_rerender = true; this.set({'value': value_}); this.no_rerender = tmp; @@ -2296,7 +2305,8 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we return this.get('value') === '' || this._super(); }, focus: function() { - this.$('input:first')[0].focus(); + var input = this.$('input:first')[0]; + return input ? input.focus() : false; }, set_dimensions: function (height, width) { this._super(height, width); @@ -2393,7 +2403,8 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ this._super.apply(this, [value_]); }, focus: function () { - this.$('input:first').select(); + var $input = this.$('input:first'); + return $input.length ? $input.select() : false; } }); @@ -2413,6 +2424,42 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({ this.$input = this.$el.find('input.oe_datepicker_master'); this.$input_picker = this.$el.find('input.oe_datepicker_container'); + $.datepicker.setDefaults({ + clearText: _t('Clear'), + clearStatus: _t('Erase the current date'), + closeText: _t('Done'), + closeStatus: _t('Close without change'), + prevText: _t('<Prev'), + prevStatus: _t('Show the previous month'), + nextText: _t('Next>'), + nextStatus: _t('Show the next month'), + currentText: _t('Today'), + currentStatus: _t('Show the current month'), + monthNames: Date.CultureInfo.monthNames, + monthNamesShort: Date.CultureInfo.abbreviatedMonthNames, + monthStatus: _t('Show a different month'), + yearStatus: _t('Show a different year'), + weekHeader: _t('Wk'), + weekStatus: _t('Week of the year'), + dayNames: Date.CultureInfo.dayNames, + dayNamesShort: Date.CultureInfo.abbreviatedDayNames, + dayNamesMin: Date.CultureInfo.shortestDayNames, + dayStatus: _t('Set DD as first week day'), + dateStatus: _t('Select D, M d'), + firstDay: Date.CultureInfo.firstDayOfWeek, + initStatus: _t('Select a date'), + isRTL: false + }); + $.timepicker.setDefaults({ + timeOnlyTitle: _t('Choose Time'), + timeText: _t('Time'), + hourText: _t('Hour'), + minuteText: _t('Minute'), + secondText: _t('Second'), + currentText: _t('Now'), + closeText: _t('Done') + }); + this.picker({ onClose: this.on_picker_select, onSelect: this.on_picker_select, @@ -2539,9 +2586,8 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc return this.get('value') === '' || this._super(); }, focus: function() { - if (this.datewidget && this.datewidget.$input) { - this.datewidget.$input[0].focus(); - } + var input = this.datewidget && this.datewidget.$input[0]; + return input ? input.focus() : false; }, set_dimensions: function (height, width) { this._super(height, width); @@ -2622,9 +2668,8 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we return this.get('value') === '' || this._super(); }, focus: function($el) { - if (!this.get("effective_readonly") && this.$textarea) { - this.$textarea[0].focus(); - } + var input = !this.get("effective_readonly") && this.$textarea && this.$textarea[0]; + return input ? input.focus() : false; }, set_dimensions: function (height, width) { this._super(height, width); @@ -2707,7 +2752,8 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({ this.$checkbox[0].checked = this.get('value'); }, focus: function() { - this.$checkbox[0].focus(); + var input = this.$checkbox && this.$checkbox[0]; + return input ? input.focus() : false; } }); @@ -2793,7 +2839,8 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan } }, focus: function() { - this.$('select:first')[0].focus(); + var input = this.$('select:first')[0]; + return input ? input.focus() : false; }, set_dimensions: function (height, width) { this._super(height, width); @@ -3336,7 +3383,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc } if (! no_recurse) { var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context()); - dataset.name_get([self.get("value")]).done(function(data) { + this.alive(dataset.name_get([self.get("value")])).done(function(data) { self.display_value["" + self.get("value")] = data[0][1]; self.render_value(true); }); @@ -3401,9 +3448,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc return ! this.get("value"); }, focus: function () { - if (!this.get('effective_readonly')) { - this.$input && this.$input[0].focus(); - } + var input = !this.get('effective_readonly') && this.$input && this.$input[0]; + return input ? input.focus() : false; }, _quick_create: function() { this.no_ed = true; @@ -4263,7 +4309,8 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in this.set({'value': _.uniq(this.get('value').concat([id]))}); }, focus: function () { - this.$text[0].focus(); + var input = this.$text && this.$text[0]; + return input ? input.focus() : false; }, set_dimensions: function (height, width) { this._super(height, width); @@ -5356,14 +5403,14 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false; this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false; this.set({value: false}); - this.selection = []; - this.set("selection", []); + this.selection = {'unfolded': [], 'folded': []}; + this.set("selection", {'unfolded': [], 'folded': []}); this.selection_dm = new instance.web.DropMisordered(); + this.dataset = new instance.web.DataSetStatic(this, this.field.relation, this.build_context()); }, start: function() { this.field_manager.on("view_content_has_changed", this, this.calc_domain); this.calc_domain(); - this.on("change:value", this, this.get_selection); this.on("change:evaluated_selection_domain", this, this.get_selection); this.on("change:selection", this, function() { this.selection = this.get("selection"); @@ -5371,7 +5418,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ }); this.get_selection(); if (this.options.clickable) { - this.$el.on('click','li',this.on_click_stage); + this.$el.on('click','li:not(.oe_folded)',this.on_click_stage); } if (this.$el.parent().is('header')) { this.$el.after('<div class="oe_clear"/>'); @@ -5386,17 +5433,20 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ }, render_value: function() { var self = this; - var content = QWeb.render("FieldStatus.content", {widget: self}); + var content = QWeb.render("FieldStatus.content", { + 'widget': self, + 'value_folded': _.find(self.selection.folded, function(i){return i[0] === self.get('value')}) + }); self.$el.html(content); }, calc_domain: function() { var d = instance.web.pyeval.eval('domain', this.build_domain()); var domain = []; //if there is no domain defined, fetch all the records - + if (d.length) { domain = ['|',['id', '=', this.get('value')]].concat(d); } - + if (! _.isEqual(domain, this.get("evaluated_selection_domain"))) { this.set("evaluated_selection_domain", domain); } @@ -5408,17 +5458,27 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ */ get_selection: function() { var self = this; - var selection = []; + var selection_unfolded = []; + var selection_folded = []; var calculation = _.bind(function() { if (this.field.type == "many2one") { - var domain = []; - var ds = new instance.web.DataSetSearch(this, this.field.relation, - self.build_context(), this.get("evaluated_selection_domain")); - return ds.read_slice(['name'], {}).then(function (records) { - for(var i = 0; i < records.length; i++) { - selection.push([records[i].id, records[i].name]); - } + return self.get_distant_fields().then(function(fields) { + return new instance.web.DataSetSearch(self, self.field.relation, self.build_context(), self.get("evaluated_selection_domain")) + .read_slice(fields.fold ? ['fold'] : ['id'], {}).then(function (records) { + + var ids = _.map(records, function (val) {return val.id}); + return self.dataset.name_get(ids).then(function (records_name) { + _.each(records, function (record) { + var name = _.find(records_name, function (val) {return val[0] == record.id;})[1]; + if (record.fold && record.id != self.get('value')) { + selection_folded.push([record.id, name]); + } else { + selection_unfolded.push([record.id, name]); + } + }); + }) + }); }); } else { // For field type selection filter values according to @@ -5428,18 +5488,29 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ for(var i=0; i < select.length; i++) { var key = select[i][0]; if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) { - selection.push(select[i]); + selection_unfolded.push(select[i]); } } return $.when(); } }, this); this.selection_dm.add(calculation()).then(function () { + var selection = {'unfolded': selection_unfolded, 'folded': selection_folded}; if (! _.isEqual(selection, self.get("selection"))) { self.set("selection", selection); } }); }, + get_distant_fields: function() { + var self = this; + if (this.distant_fields) { + return $.when(this.distant_fields); + } + return new instance.web.Model(self.field.relation).call("fields_get", [["fold"]]).then(function(fields) { + self.distant_fields = fields; + return fields; + }) + }, on_click_stage: function (ev) { var self = this; var $li = $(ev.currentTarget); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index b69ec6dbdca..b96079d2051 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -578,7 +578,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.no_leaf = !!context['group_by_no_leaf']; this.grouped = !!group_by; - return this.load_view(context).then( + return this.alive(this.load_view(context)).then( this.proxy('reload_content')); }, /** @@ -895,8 +895,9 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. this.record_callbacks = { 'remove': function (event, record) { - var $row = self.$current.children( - '[data-id=' + record.get('id') + ']'); + var id = record.get('id'); + self.dataset.remove_ids([id]) + var $row = self.$current.children('[data-id=' + id + ']'); var index = $row.data('index'); $row.remove(); }, diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 1143bdfc2af..0737281e199 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -241,6 +241,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ } else if (state.active_id) { add_context.active_ids = [state.active_id]; } + add_context.params = state; this.null_action(); action_loaded = this.do_action(state.action, { additional_context: add_context }); $.when(action_loaded || null).done(function() { @@ -1004,7 +1005,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({ switch_mode: function (view_type, no_store, options) { var self = this; - return $.when(this._super.apply(this, arguments)).done(function () { + return this.alive($.when(this._super.apply(this, arguments))).done(function () { var controller = self.views[self.active_view].controller; self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', { view: controller, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 943326f5011..14c7f722ec2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -650,7 +650,7 @@ <li t-if="section.name == 'files'" class="oe_sidebar_add_attachment"> <t t-call="HiddenInputFile"> <t t-set="fileupload_id" t-value="widget.fileupload_id"/> - <t t-set="fileupload_action">/web/binary/upload_attachment</t> + <t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t> <input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/> <input type="hidden" name="id" t-att-value="widget.model_id"/> <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/> @@ -1218,13 +1218,25 @@ <ul t-att-class="'oe_form_field_status ' + (widget.options.clickable ? 'oe_form_status_clickable' : 'oe_form_status')" t-att-style="widget.node.attrs.style"/> </t> <t t-name="FieldStatus.content"> - <t t-foreach="widget.selection" t-as="i"> + <t t-foreach="widget.selection.unfolded" t-as="i"> <li t-att-class="i[0] === widget.get('value') ? 'oe_active' : ''" t-att-data-id="i[0]"> <span class="label"><t t-esc="i[1]"/></span> <!-- are you mit ? --> <span class="arrow"><span></span></span> </li> </t> + <t t-if="widget.selection.folded.length"> + <li t-att-class="value_folded ? 'oe_active oe_folded' : 'oe_folded'"> + <span class="label"> ... </span> + <ul> + <t t-foreach="widget.selection.folded" t-as="i"> + <li t-att-class="i[0] === widget.get('value') ? 'oe_active' : ''" t-att-data-id="i[0]"> + <span class="label"><t t-esc="i[1]"/></span> + </li> + </t> + </ul> + </li> + </t> </t> <t t-name="FieldBinaryImage"> <span class="oe_form_field oe_form_field_image" t-att-style="widget.node.attrs.style"> @@ -1552,8 +1564,9 @@ <t t-esc="attrs.string"/> </button> <ul t-name="SearchView.filters"> - <li t-foreach="widget.filters" t-as="filter" t-if="filter.visible()" - t-att-title="filter.attrs.string ? filter.attrs.help : undefined"> + <li t-foreach="widget.filters" t-as="filter" t-if="!filter.visible || filter.visible()" + t-att-title="filter.attrs.string ? filter.attrs.help : undefined" + t-att-data-index="filter_index"> <t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/> </li> </ul> |
