summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-04-19 10:12:13 +0200
committerXavier Morel <xmo@openerp.com>2012-04-19 10:12:13 +0200
commite7ab44929ff4aa3add8d00bab97fc3e879b9b91c (patch)
treea600579e5c3c00c473550db8da591b6634fcbe0f /addons/web/static/src
parent45c48d684270f9a88af668f99a1d027df57f68d3 (diff)
parentf5c37eabe94a04ca62d08f46c1cf3a084fa77f19 (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20120419081213-m3fhofkgydj6gp1m
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/Makefile3
-rw-r--r--addons/web/static/src/css/base.css1687
-rw-r--r--addons/web/static/src/css/base.sass1354
-rw-r--r--addons/web/static/src/css/base_old.css1049
-rwxr-xr-xaddons/web/static/src/font/mnmliconsv21-webfont.eotbin0 -> 24173 bytes
-rwxr-xr-xaddons/web/static/src/font/mnmliconsv21-webfont.svg149
-rwxr-xr-xaddons/web/static/src/font/mnmliconsv21-webfont.ttfbin0 -> 39912 bytes
-rwxr-xr-xaddons/web/static/src/font/mnmliconsv21-webfont.woffbin0 -> 26348 bytes
-rw-r--r--addons/web/static/src/img/form_sheetbg.pngbin0 -> 97 bytes
-rw-r--r--addons/web/static/src/img/form_steps.pngbin0 -> 361 bytes
-rw-r--r--addons/web/static/src/img/list_empty_arrow.pngbin0 -> 2308 bytes
-rw-r--r--addons/web/static/src/img/topbar-avatar.pngbin846 -> 0 bytes
-rw-r--r--addons/web/static/src/js/boot.js21
-rw-r--r--addons/web/static/src/js/chrome.js465
-rw-r--r--addons/web/static/src/js/corelib.js (renamed from addons/web/static/src/js/core.js)1306
-rw-r--r--addons/web/static/src/js/coresetup.js142
-rw-r--r--addons/web/static/src/js/data.js120
-rw-r--r--addons/web/static/src/js/data_export.js10
-rw-r--r--addons/web/static/src/js/data_import.js16
-rw-r--r--addons/web/static/src/js/dates.js14
-rw-r--r--addons/web/static/src/js/formats.js86
-rw-r--r--addons/web/static/src/js/search.js1496
-rw-r--r--addons/web/static/src/js/view_editor.js68
-rw-r--r--addons/web/static/src/js/view_form.js2829
-rw-r--r--addons/web/static/src/js/view_list.js303
-rw-r--r--addons/web/static/src/js/view_list_editable.js114
-rw-r--r--addons/web/static/src/js/view_page.js288
-rw-r--r--addons/web/static/src/js/view_tree.js21
-rw-r--r--addons/web/static/src/js/views.js521
-rw-r--r--addons/web/static/src/xml/base.xml1597
30 files changed, 7955 insertions, 5704 deletions
diff --git a/addons/web/static/src/css/Makefile b/addons/web/static/src/css/Makefile
new file mode 100644
index 00000000000..eca044497ef
--- /dev/null
+++ b/addons/web/static/src/css/Makefile
@@ -0,0 +1,3 @@
+base.css: base.sass
+ sass -t expanded base.sass base.css
+
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index b0a2b7945b9..854946cdf97 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -1,37 +1,428 @@
-.openerp2 {
+@charset "utf-8";
+@font-face {
+ font-family: "mnmliconsRegular";
+ src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
+ src: url("/web/static/src/font/mnmliconsv21-webfont.woff") format("woff");
+ src: url("/web/static/src/font/mnmliconsv21-webfont.ttf") format("truetype");
+ src: url("/web/static/src/font/mnmliconsv21-webfont.svg") format("svg") active;
+ font-weight: normal;
+ font-style: normal;
+}
+
+.openerp.openerp-web-client-container {
+ height: 100%;
+ position: relative;
+}
+
+.openerp {
padding: 0;
margin: 0;
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
color: #4c4c4c;
font-size: 13px;
background: white;
- position: relative;
+ /* http://www.quirksmode.org/dom/inputfile.html
+ * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
+ */
}
-.openerp2 a {
+.openerp a {
text-decoration: none;
}
-.openerp2 .oe_webclient {
- position: absolute;
+.openerp table {
+ padding: 0;
+ font-size: 13px;
+ border-collapse: collapse;
+}
+.openerp thead {
+ font-weight: bold;
+ background-color: #f0f0f0;
+}
+.openerp thead th {
+ border-right: 1px dotted #afafb6;
+}
+.openerp thead th:last-child {
+ border-right: none;
+}
+.openerp th, .openerp td {
+ padding: 0;
+ line-height: 18px;
+ text-align: left;
+}
+.openerp th {
+ font-weight: bold;
+ vertical-align: middle;
+}
+.openerp td {
+ vertical-align: top;
+}
+.openerp .zebra tbody tr:nth-child(odd) td {
+ background-color: #f0f0fa;
+ background-color: #f0f0fa;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
+ background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
+}
+.openerp .zebra tbody tr:hover td {
+ background-color: #eeeeee;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -moz-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -ms-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -o-linear-gradient(top, #eeeeee, #dedede);
+ background-image: linear-gradient(to bottom, #eeeeee, #dedede);
+}
+.openerp .numeric {
+ text-align: right;
+ width: 82px;
+}
+.openerp .numeric input {
+ text-align: right;
+}
+.openerp ul, .openerp li, .openerp ol {
+ margin: 0;
+ padding: 0;
+}
+.openerp li {
+ list-style-type: none;
+}
+.openerp .oe_i {
+ font-family: "mnmliconsRegular" !important;
+ font-size: 21px;
+ font-weight: 300 !important;
+}
+.openerp .oe_left {
+ float: left;
+ margin-right: 8px;
+}
+.openerp .oe_right {
+ float: right;
+ margin-left: 8px;
+}
+.openerp .oe_clear {
+ clear: both;
+}
+.openerp .oe_wait {
+ cursor: wait;
+}
+.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp input[type='submit'] {
+ display: inline-block;
+ border: 1px solid #ababab;
+ color: #404040;
+ margin: 0;
+ padding: 4px 12px;
+ font-size: 13px;
+ text-align: center;
+ background-color: #efefef;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8));
+ background-image: -webkit-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -moz-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -ms-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -o-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: linear-gradient(to bottom, #efefef, #d8d8d8);
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
+ -webkit-font-smoothing: antialiased;
+ outline: none;
+}
+.openerp a.button:hover, .openerp button:hover, .openerp input[type='submit']:hover {
+ background-color: #f6f6f6;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
+ background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -moz-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -ms-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
+ cursor: pointer;
+}
+.openerp a.button:focus, .openerp button:focus, .openerp input[type='submit']:focus {
+ border: 1px solid #80bfff;
+ background-color: #f6f6f6;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
+ background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -moz-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -ms-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
+ -moz-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -webkit-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+}
+.openerp a.button:active, .openerp a.button.active, .openerp button:active, .openerp button.active, .openerp input[type='submit']:active, .openerp input[type='submit'].active {
+ background: #e3e3e3;
+ background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
+ background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f;
+ background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp a.button.disabled, .openerp button:disabled, .openerp input[type='submit']:disabled {
+ background: #efefef !important;
+ border: 1px solid #d1d1d1 !important;
+ -moz-box-shadow: none !important;
+ -webkit-box-shadow: none !important;
+ -box-shadow: none !important;
+ color: #aaaaaa !important;
+ cursor: default;
+ text-shadow: 0 1px 1px white !important;
+}
+.openerp button.primary {
+ color: white;
+ background-color: #dc5f59;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
+ background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -o-linear-gradient(top, #dc5f59, #b33630);
+ background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp button.primary:hover {
+ background-color: #df6b66;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33));
+ background-image: -webkit-linear-gradient(top, #df6b66, #bf3a33);
+ background-image: -moz-linear-gradient(top, #df6b66, #bf3a33);
+ background-image: -ms-linear-gradient(top, #df6b66, #bf3a33);
+ background-image: -o-linear-gradient(top, #df6b66, #bf3a33);
+ background-image: linear-gradient(to bottom, #df6b66, #bf3a33);
+ -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
+}
+.openerp button.primary:active {
+ background-color: #b33630;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#b33630), to(#dc5f59));
+ background-image: -webkit-linear-gradient(top, #b33630, #dc5f59);
+ background-image: -moz-linear-gradient(top, #b33630, #dc5f59);
+ background-image: -ms-linear-gradient(top, #b33630, #dc5f59);
+ background-image: -o-linear-gradient(top, #b33630, #dc5f59);
+ background-image: linear-gradient(to bottom, #b33630, #dc5f59);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp button.following {
+ color: white;
+ width: 120px;
+ background-color: #8a89ba;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4));
+ background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4);
+ background-image: -moz-linear-gradient(top, #8a89ba, #807fb4);
+ background-image: -ms-linear-gradient(top, #8a89ba, #807fb4);
+ background-image: -o-linear-gradient(top, #8a89ba, #807fb4);
+ background-image: linear-gradient(to bottom, #8a89ba, #807fb4);
+}
+.openerp button.unfollow {
+ display: none;
+ color: white;
+ width: 120px;
+ background-color: #dc5f59;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
+ background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
+ background-image: -o-linear-gradient(top, #dc5f59, #b33630);
+ background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+}
+.openerp .oe_loading {
+ display: none;
+ z-index: 100;
+ position: fixed;
top: 0;
- bottom: 0;
+ right: 50%;
+ padding: 4px 12px;
+ background: #a61300;
+ color: white;
+ text-align: center;
+ border: 1px solid #990000;
+ border-top: none;
+ -moz-border-radius-bottomright: 8px;
+ -moz-border-radius-bottomleft: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-left-radius: 8px;
+}
+.openerp .oe_notification {
+ z-index: 1050;
+}
+.openerp .oe_login {
+ background: url("/web/static/src/img/pattern.png") repeat;
+ text-align: center;
+ font-size: 14px;
+ height: 100%;
+}
+.openerp .oe_login li {
+ list-style-type: none;
+ padding-bottom: 4px;
+}
+.openerp .oe_login button {
+ float: right;
+ display: inline-block;
+ cursor: pointer;
+ padding: 6px 16px;
+ border: 1px solid #222222;
+ color: white;
+ margin: 0;
+ background-color: #b92020;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#b92020), to(#600606));
+ background-image: -webkit-linear-gradient(top, #b92020, #600606);
+ background-image: -moz-linear-gradient(top, #b92020, #600606);
+ background-image: -ms-linear-gradient(top, #b92020, #600606);
+ background-image: -o-linear-gradient(top, #b92020, #600606);
+ background-image: linear-gradient(to bottom, #b92020, #600606);
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
+}
+.openerp .oe_login input, .openerp .oe_login select {
+ width: 252px;
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial;
+ border: 1px solid #999999;
+ background: whitesmoke;
+ -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
+ -box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+.openerp .oe_login input {
+ margin-bottom: 9px;
+ padding: 5px 6px;
+}
+.openerp .oe_login select {
+ padding: 1px;
+}
+.openerp .oe_login .oe_login_dbpane {
+ position: fixed;
+ top: 0;
+ right: 8px;
+ padding: 5px 10px;
+ color: #eeeeee;
+ border: solid 1px #333333;
+ background: #1e1e1e;
+ background: rgba(30, 30, 30, 0.94);
+ -moz-border-radius: 0 0 8px 8px;
+ -webkit-border-radius: 0 0 8px 8px;
+ border-radius: 0 0 8px 8px;
+}
+.openerp .oe_login .oe_login_dbpane input {
+ padding: 2px 4px;
+ margin: 4px 0;
+}
+.openerp .oe_login .oe_login_bottom {
+ position: absolute;
+ top: 50%;
left: 0;
right: 0;
+ bottom: 0;
+ text-shadow: 0 1px 1px #999999;
+ background-color: #b41616;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606));
+ background-image: -webkit-linear-gradient(top, #b41616, #600606);
+ background-image: -moz-linear-gradient(top, #b41616, #600606);
+ background-image: -ms-linear-gradient(top, #b41616, #600606);
+ background-image: -o-linear-gradient(top, #b41616, #600606);
+ background-image: linear-gradient(to bottom, #b41616, #600606);
}
-.openerp2 .oe_webclient .oe_application {
+.openerp .oe_login .oe_login_pane {
position: absolute;
- top: 32px;
- bottom: 0;
- left: 206px;
- right: 0;
+ top: 50%;
+ left: 50%;
+ margin: -160px -166px;
+ border: solid 1px #333333;
+ background: #1e1e1e;
+ background: rgba(30, 30, 30, 0.94);
+ padding: 22px 32px;
+ color: #eeeeee;
+ text-align: left;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ -moz-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
+ -webkit-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
+ -box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
+}
+.openerp .oe_login .oe_login_pane h2 {
+ margin-top: 0;
+ font-size: 18px;
+}
+.openerp .oe_login .oe_login_logo {
+ position: absolute;
+ top: -70px;
+ left: 0;
+ width: 100%;
+ margin: 0 auto;
+ text-align: center;
+}
+.openerp .oe_login .oe_login_footer {
+ position: absolute;
+ bottom: -40px;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.openerp .oe_login .oe_login_footer a {
+ color: #eeeeee;
+ margin: 0 8px;
+}
+.openerp .oe_login .oe_login_footer a:hover {
+ text-decoration: underline;
+}
+.openerp .oe_login .oe_login_footer span {
+ font-weight: bold;
+ font-size: 16px;
+}
+.openerp .oe_login .oe_login_error_message {
+ display: none;
+ background-color: #b41616;
+ color: #eeeeee;
+ padding: 14px 18px;
+ margin-top: 15px;
+ text-align: center;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
+}
+.openerp .oe_login_invalid .oe_login_error_message {
+ display: inline-block;
+}
+.openerp .oe_database_manager {
+ background: white;
+ color: black;
+ text-align: left;
+}
+.openerp .oe_database_manager .oe_database_manager_menu {
+ color: black;
}
-.openerp2 .oe_content_full_screen .oe_application {
+.openerp .oe_webclient {
+ width: 100%;
+ height: 100%;
+ border-spacing: 0px;
+}
+.openerp .oe_content_full_screen .oe_application {
top: 0;
left: 0;
}
-.openerp2 .oe_content_full_screen .topbar, .openerp2 .oe_content_full_screen .leftbar {
+.openerp .oe_content_full_screen .oe_topbar, .openerp .oe_content_full_screen .oe_leftbar {
display: none;
}
-.openerp2 .oe_topbar {
+.openerp .oe_topbar {
width: 100%;
height: 31px;
border-top: solid 1px #d3d3d3;
@@ -44,17 +435,17 @@
background-image: -o-linear-gradient(top, #646060, #262626);
background-image: linear-gradient(to bottom, #646060, #262626);
}
-.openerp2 .oe_topbar .oe_systray {
+.openerp .oe_topbar .oe_systray {
float: right;
}
-.openerp2 .oe_topbar .oe_systray > div {
+.openerp .oe_topbar .oe_systray > div {
float: left;
padding: 0 4px 0 4px;
}
-.openerp2 .oe_topbar .oe_topbar_item li {
+.openerp .oe_topbar .oe_topbar_item li {
float: left;
}
-.openerp2 .oe_topbar .oe_topbar_item li a {
+.openerp .oe_topbar .oe_topbar_item li a {
display: block;
padding: 5px 10px 7px;
line-height: 20px;
@@ -63,14 +454,14 @@
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
-.openerp2 .oe_topbar .oe_topbar_item li a:hover {
+.openerp .oe_topbar .oe_topbar_item li a:hover {
background: #303030;
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
}
-.openerp2 .oe_topbar .oe_topbar_item .oe_active {
+.openerp .oe_topbar .oe_topbar_item .oe_active {
background: #303030;
font-weight: bold;
color: white;
@@ -78,7 +469,7 @@
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
}
-.openerp2 .oe_topbar .oe_topbar_avatar {
+.openerp .oe_topbar .oe_topbar_avatar {
width: 24px;
height: 24px;
margin: -2px 2px 0 0;
@@ -86,60 +477,134 @@
-webkit-border-radius: 4px;
border-radius: 4px;
}
-.openerp2 .oe_topbar .oe_topbar_avatar {
+.openerp .oe_topbar .oe_topbar_avatar {
vertical-align: top;
}
-.openerp2 .oe_leftbar {
- width: 205px;
- height: 100%;
+.openerp .oe_topbar .oe_dropdown_toggle:after {
+ width: 0;
+ height: 0;
+ display: inline-block;
+ content: "&darr";
+ text-indent: -99999px;
+ vertical-align: top;
+ margin-top: 8px;
+ margin-left: 4px;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid white;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
+}
+.openerp .oe_leftbar {
+ width: 220px;
background: #f0eeee;
border-right: 1px solid #afafb6;
- overflow: auto;
text-shadow: 0 1px 1px white;
}
-.openerp2 .oe_leftbar .oe_footer {
- position: absolute;
- width: 205px;
- text-align: center;
- bottom: 8px;
-}
-.openerp2 a.oe_logo {
+.openerp a.oe_logo {
+ width: 220px;
display: block;
text-align: center;
height: 70px;
line-height: 70px;
}
-.openerp2 a.oe_logo img {
+.openerp a.oe_logo img {
height: 40px;
width: 157px;
margin: 14px 0;
}
-.openerp2 .oe_footer {
- position: absolute;
- width: 205px;
+.openerp .oe_footer {
+ position: fixed;
+ bottom: 0;
+ padding: 4px 0;
+ background: #f0eeee;
+ width: 220px;
text-align: center;
- bottom: 8px;
}
-.openerp2 .oe_footer a {
+.openerp .oe_footer a {
font-weight: 800;
font-family: serif;
font-size: 16px;
color: black;
}
-.openerp2 .oe_footer a span {
+.openerp .oe_footer a span {
color: #c81010;
font-style: italic;
}
-.openerp2 .oe_menu {
- float: left;
+.openerp .oe_user_menu {
+ float: right;
padding: 0;
margin: 0;
}
-.openerp2 .oe_menu li {
+.openerp .oe_user_menu li {
list-style-type: none;
float: left;
}
-.openerp2 .oe_menu a {
+.openerp .oe_user_menu .oe_dropdown {
+ position: relative;
+}
+.openerp .oe_user_menu .oe_dropdown_options {
+ float: left;
+ background: #333333;
+ background: rgba(37, 37, 37, 0.9);
+ display: none;
+ position: absolute;
+ top: 32px;
+ right: -1px;
+ border: 0;
+ z-index: 900;
+ margin-left: 0;
+ margin-right: 0;
+ padding: 6px 0;
+ zoom: 1;
+ border-color: #999999;
+ border-color: rgba(0, 0, 0, 0.2);
+ border-style: solid;
+ border-width: 0 1px 1px;
+ -moz-border-radius: 0 0 6px 6px;
+ -webkit-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+.openerp .oe_user_menu .oe_dropdown_options li {
+ float: none;
+ display: block;
+ background-color: none;
+}
+.openerp .oe_user_menu .oe_dropdown_options li a {
+ display: block;
+ padding: 4px 15px;
+ clear: both;
+ font-weight: normal;
+ line-height: 18px;
+ color: #eeeeee;
+}
+.openerp .oe_user_menu .oe_dropdown_options li a:hover {
+ background-color: #292929;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
+ background-image: -webkit-linear-gradient(top, #292929, #191919);
+ background-image: -moz-linear-gradient(top, #292929, #191919);
+ background-image: -ms-linear-gradient(top, #292929, #191919);
+ background-image: -o-linear-gradient(top, #292929, #191919);
+ background-image: linear-gradient(to bottom, #292929, #191919);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp .oe_menu {
+ float: left;
+ padding: 0;
+ margin: 0;
+}
+.openerp .oe_menu li {
+ float: left;
+}
+.openerp .oe_menu a {
display: block;
padding: 5px 10px 7px;
line-height: 20px;
@@ -148,14 +613,14 @@
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
-.openerp2 .oe_menu a:hover {
+.openerp .oe_menu a:hover {
background: #303030;
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
}
-.openerp2 .oe_menu .oe_active {
+.openerp .oe_menu .oe_active {
background: #303030;
font-weight: bold;
color: white;
@@ -163,10 +628,10 @@
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
}
-.openerp2 .oe_menu_more_container {
+.openerp .oe_menu_more_container {
position: relative;
}
-.openerp2 .oe_menu_more_container .oe_menu_more {
+.openerp .oe_menu_more_container .oe_menu_more {
position: absolute;
padding: 0;
background-color: #646060;
@@ -175,34 +640,33 @@
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
-.openerp2 .oe_menu_more_container .oe_menu_more li {
+.openerp .oe_menu_more_container .oe_menu_more li {
float: none;
}
-.openerp2 .oe_menu_more_container .oe_menu_more li a {
+.openerp .oe_menu_more_container .oe_menu_more li a {
white-space: nowrap;
}
-.openerp2 .oe_secondary_menu_section {
+.openerp .oe_secondary_menu_section {
font-weight: bold;
margin-left: 8px;
color: #8a89ba;
}
-.openerp2 .oe_secondary_submenu {
+.openerp .oe_secondary_submenu {
padding: 2px 0 8px 0;
margin: 0;
width: 100%;
display: inline-block;
}
-.openerp2 .oe_secondary_submenu li {
+.openerp .oe_secondary_submenu li {
position: relative;
padding: 1px 0 1px 16px;
- list-style-type: none;
}
-.openerp2 .oe_secondary_submenu li a {
+.openerp .oe_secondary_submenu li a {
display: block;
color: #4c4c4c;
padding: 2px 4px 2px 0;
}
-.openerp2 .oe_secondary_submenu li .oe_menu_label {
+.openerp .oe_secondary_submenu li .oe_menu_label {
position: absolute;
top: 1px;
right: 1px;
@@ -220,7 +684,20 @@
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
}
-.openerp2 .oe_secondary_submenu .oe_active {
+.openerp .oe_secondary_submenu .oe_menu_counter {
+ float: right;
+ background: #8a89ba;
+ color: #eeeeee;
+ font-size: 12px;
+ border: 1px solid lightgray;
+ padding: 0px 4px 0px 4px;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ margin: 0px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+}
+.openerp .oe_secondary_submenu .oe_active {
background: #8a89ba;
border-top: 1px solid lightGray;
border-bottom: 1px solid lightGray;
@@ -229,10 +706,10 @@
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
}
-.openerp2 .oe_secondary_submenu .oe_active a {
+.openerp .oe_secondary_submenu .oe_active a {
color: white;
}
-.openerp2 .oe_secondary_submenu .oe_active .oe_menu_label {
+.openerp .oe_secondary_submenu .oe_active .oe_menu_label {
background: #eeeeee;
color: #8a89ba;
text-shadow: 0 1px 1px white;
@@ -240,7 +717,11 @@
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
-.openerp2 .oe_secondary_submenu .oe_menu_toggler:before {
+.openerp .oe_secondary_submenu .oe_active .oe_menu_counter {
+ background: #eeeeee;
+ color: #8a89ba;
+}
+.openerp .oe_secondary_submenu .oe_menu_toggler:before {
width: 0;
height: 0;
display: inline-block;
@@ -256,7 +737,7 @@
filter: alpha(opacity=50);
opacity: 0.5;
}
-.openerp2 .oe_secondary_submenu .oe_menu_opened:before {
+.openerp .oe_secondary_submenu .oe_menu_opened:before {
margin-top: 6px;
margin-left: -12px;
margin-right: 4px;
@@ -264,83 +745,1075 @@
border-right: 4px solid transparent;
border-top: 4px solid #4c4c4c;
}
-.openerp2 .oe_dropdown {
+.openerp .oe_application {
+ width: 100%;
+}
+.openerp .oe_application a {
+ color: #8a89ba;
+}
+.openerp .oe_application a:hover {
+ text-decoration: underline;
+}
+.openerp .oe-view-manager-header {
+ border-top: 1px solid #cacaca;
+ border-bottom: 1px solid #cacaca;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+ -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+}
+.openerp .oe-view-manager-header h2 {
+ float: left;
+ font-size: 18px;
+ margin: 1px 0;
+}
+.openerp .oe-view-manager-header h2 a {
+ color: #8a89ba;
+}
+.openerp .oe-view-manager-header .buttons {
+ padding-right: 8px;
+}
+.openerp .oe-view-manager-header .buttons li {
+ float: left;
+ margin-right: 12px;
+}
+.openerp .oe-view-manager-header .filter {
+ vertical-align: top;
+}
+.openerp .oe-view-manager-header .filter li {
+ height: 24px;
+ line-height: 24px;
+ padding: 0 8px;
+}
+.openerp .oe-view-manager-header .header-row {
+ height: 26px;
+ line-height: 26px;
+ margin: 8px;
+ clear: both;
+ text-shadow: 0 1px 1px white;
+}
+.openerp .oe-view-manager-header .header-row .dropdown-toggle:after {
+ margin-top: 7px;
+ margin-left: 6px;
+ border-top-color: #404040;
+}
+.openerp .oe-view-manager-header .oe_vm_switch li {
+ padding-left: 1px;
+ text-align: center;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+}
+.openerp .oe-view-manager-header .pagination li {
+ height: 24px;
+ line-height: 24px;
+ padding: 0 8px;
+}
+.openerp .oe-view-manager-header .button-group {
+ display: inline-block;
+ border: 1px solid #ababab;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+.openerp .oe-view-manager-header .button-group li {
+ float: left;
+ border-right: 1px solid #ababab;
+}
+.openerp .oe-view-manager-header .button-group li:last-child {
+ border: none;
+}
+.openerp .oe-view-manager-header .button-group a {
+ color: #4c4c4c;
+}
+.openerp .oe-view-manager-header .button-group a:hover {
+ text-decoration: none;
+}
+.openerp .oe-view-manager-header .button-group .active {
+ width: 100%;
+ background: #999999;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ color: white;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
+}
+.openerp .oe_view_manager_header2 {
+ width: 100%;
+ border-top: 1px solid #cacaca;
+ border-bottom: 1px solid #cacaca;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+ -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+}
+.openerp .oe_view_manager_header2 h2 {
+ float: left;
+ font-size: 18px;
+ margin: 1px 0;
+}
+.openerp .oe_view_manager_header2 h2 a {
+ color: #8a89ba;
+}
+.openerp .oe_view_manager_header2 td {
+ padding: 4px 8px;
+ line-height: 26px;
+}
+.openerp .oe_view_manager_header2 .oe_button_group {
+ display: inline-block;
+ border: 1px solid #ababab;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+.openerp .oe_view_manager_header2 .oe_button_group li {
+ float: left;
+ border-right: 1px solid #ababab;
+}
+.openerp .oe_view_manager_header2 .oe_button_group li:last-child {
+ border: none;
+}
+.openerp .oe_view_manager_header2 .oe_button_group a {
+ color: #4c4c4c;
+}
+.openerp .oe_view_manager_header2 .oe_button_group a:hover {
+ text-decoration: none;
+}
+.openerp .oe_view_manager_header2 .oe_button_group .active {
+ background: #999999;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+}
+.openerp .oe_view_manager_header2 .oe_button_group .active a {
+ color: white;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
+}
+.openerp .oe_view_manager_header2 .oe_view_manager_buttons {
+ white-space: nowrap;
+}
+.openerp .oe_view_manager_body h4 {
+ margin: 8px 0;
+}
+.openerp .oe_pager_value {
+ float: left;
+ margin-right: 8px;
+}
+.openerp .oe_pager_group {
+ float: left;
+ height: 24px;
+ line-height: 24px;
+ display: inline-block;
+ border: 1px solid #ababab;
+ cursor: pointer;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+.openerp .oe_pager_group li {
+ height: 24px;
+ line-height: 24px;
+ padding: 0;
+ float: left;
+ border-right: 1px solid #ababab;
+}
+.openerp .oe_pager_group li:last-child {
+ border: none;
+}
+.openerp .oe_pager_group a {
+ color: #4c4c4c;
+ padding: 0 8px;
+}
+.openerp .oe_pager_group a:hover {
+ text-decoration: none;
+}
+.openerp .oe_pager_group .active {
+ background: #999999;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+}
+.openerp .oe_pager_group .active a {
+ color: white;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
+}
+.openerp .oe_view_manager_switch li {
+ padding-left: 1px;
+ text-align: center;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_list:after, .openerp .oe_view_manager_switch .oe_vm_switch_tree:after {
+ content: "ö";
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_graph:after {
+ content: "ó";
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_gantt:after {
+ content: "y";
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_calendar:after {
+ content: "b";
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_kanban:after {
+ content: "ó";
+}
+.openerp .oe_view_manager_switch .oe_vm_switch_diagram:after {
+ content: "}";
+}
+.openerp .oe_form_dropdown_section {
position: relative;
+ display: inline-block;
+ margin-right: 4px;
}
-.openerp2 .oe_dropdown_toggle:after {
+.openerp .oe_dropdown_toggle:after {
width: 0;
height: 0;
display: inline-block;
content: "&darr";
text-indent: -99999px;
vertical-align: top;
- margin-top: 8px;
- margin-left: 4px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid white;
filter: alpha(opacity=50);
opacity: 0.5;
+ margin-top: 7px;
+ margin-left: 6px;
+ border-top-color: #404040;
}
-.openerp2 .oe_user_menu {
- float: right;
- padding: 0;
- margin: 0;
-}
-.openerp2 .oe_user_menu li {
- list-style-type: none;
- float: left;
-}
-.openerp2 .oe_user_menu .oe_dropdown_options {
- float: left;
- background: #333333;
- background: rgba(37, 37, 37, 0.9);
+.openerp .oe_dropdown_menu {
display: none;
position: absolute;
- top: 32px;
- right: -1px;
- border: 0;
+ top: 28px;
+ left: 0px;
+ padding: 8px;
+ border: 1px solid #afafb6;
+ width: 120px;
+ overflow-x: hidden;
z-index: 900;
- margin-left: 0;
- margin-right: 0;
- padding: 6px 0;
- zoom: 1;
- border-color: #999999;
- border-color: rgba(0, 0, 0, 0.2);
- border-style: solid;
- border-width: 0 1px 1px;
- -moz-border-radius: 0 0 6px 6px;
- -webkit-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
+ text-align: left;
+ background: white;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding-box;
- background-clip: padding-box;
}
-.openerp2 .oe_user_menu .oe_dropdown_options li {
+.openerp .oe_dropdown_menu li {
+ list-style-type: none;
float: none;
display: block;
background-color: none;
}
-.openerp2 .oe_user_menu .oe_dropdown_options li a {
+.openerp .oe_dropdown_menu li a {
display: block;
- padding: 4px 15px;
+ padding: 3px 6px;
clear: both;
font-weight: normal;
+ line-height: 14px;
+ color: #4c4c4c;
+ text-decoration: none;
+}
+.openerp .oe_dropdown_menu li a:hover {
+ text-decoration: none;
+ background-color: #f0f0fa;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
+ background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp .oe_sidebar {
+ white-space: nowrap;
+}
+.openerp .oe_searchview {
+ position: relative;
+ float: right;
+}
+.openerp .oe_searchview .VS-search .VS-search-box {
+ min-height: 0;
+ border-width: 1px;
+ padding: 0;
+ width: 410px;
+}
+.openerp .oe_searchview .VS-search .VS-icon {
+ top: 5px;
+}
+.openerp .oe_searchview .VS-search .VS-icon-search {
+ left: 5px;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner {
+ margin: 0 40px 0 17px;
+ font-size: 13px;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner input {
+ font-size: inherit;
+ line-height: inherit;
+ height: auto;
+ padding: 0;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .VS-input-width-tester {
+ font-size: inherit;
+ padding: 0;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_input {
+ margin-left: 0;
+ height: 22px;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_input input, .openerp .oe_searchview .VS-search .VS-search-inner .search_input .VS-input-width-tester {
+ padding: 0 2px;
+ height: inherit;
+ margin: 0;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet {
+ padding: 0;
+ margin: 1px 0;
+ border: 1px solid #afafb6;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: #f0f0fa;
+ height: 18px;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected {
+ border-color: #a6a6fe;
+ -moz-box-shadow: 0 0 3px 1px #a6a6fe;
+ -webkit-box-shadow: 0 0 3px 1px #a6a6fe;
+ -box-shadow: 0 0 3px 1px #a6a6fe;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .category {
+ margin-left: 0;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .VS-icon-cancel {
+ filter: alpha(opacity=100);
+ opacity: 1;
+ background-position: center 0;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .category {
+ padding: 0 4px 0 14px;
+ color: white;
+ background: #8786b7;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
+ font-weight: normal;
+ text-transform: none;
+ height: 18px;
line-height: 18px;
- color: #eeeeee;
+ font-size: inherit;
}
-.openerp2 .oe_user_menu .oe_dropdown_options li a:hover {
- background-color: #292929;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
- background-image: -webkit-linear-gradient(top, #292929, #191919);
- background-image: -moz-linear-gradient(top, #292929, #191919);
- background-image: -ms-linear-gradient(top, #292929, #191919);
- background-image: -o-linear-gradient(top, #292929, #191919);
- background-image: linear-gradient(to bottom, #292929, #191919);
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_input_container {
+ border-left: 1px solid #afafb6;
+ height: 18px;
+ line-height: 18px;
+ padding: 0 4px;
+ cursor: pointer;
+}
+.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_remove {
+ left: 1px;
+ top: 3px;
+}
+.openerp .oe_searchview .VS-search .VS-icon-cancel {
+ right: 24px;
+}
+.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 100%;
+ line-height: 23px;
+ padding: 0 7px 0 4px;
+ color: #cccccc;
+ cursor: pointer;
+}
+.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer:before {
+ content: "▾";
+}
+.openerp .oe_searchview.oe_searchview_open_drawer .oe_searchview_drawer {
+ display: block;
+}
+.openerp .oe_searchview .oe_searchview_drawer {
+ position: absolute;
+ z-index: 1;
+ top: 100%;
+ right: 0;
+ background-color: white;
+ width: 410px;
+ display: none;
+ border: 1px solid #cccccc;
+ text-align: left;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters {
+ display: table;
+ width: 100%;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters > div {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ display: table-cell;
+ width: 50%;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters h3 {
+ margin: 2px 4px 2px 8px;
+ color: #8786b7;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters ul {
+ margin: 0 12px 3px;
+ padding: 0;
+ list-style: none;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li {
+ list-style: none;
+ padding: 3px 6px;
+ height: 14px;
+ color: inherit;
+ cursor: pointer;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li:hover {
+ background-color: #f0f0fa;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4 {
+ border-top: 1px solid #cccccc;
+ margin: 5px 0 3px;
+ cursor: pointer;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4:before {
+ content: "▸ ";
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced div {
+ display: none;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_add_condition:before {
+ content: "⊞ ";
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_apply:before {
+ content: "⚡ ";
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced ul {
+ list-style: none;
+ padding: 0;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li {
+ list-style: none;
+ margin: 0;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened h4:before {
+ content: "▾ ";
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened div {
+ display: block;
+}
+.openerp .oe_view_topbar {
+ border-bottom: 1px solid #cacaca;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+ padding: 0 8px;
+ line-height: 30px;
+}
+.openerp .oe_formview {
+ background: white;
+}
+.openerp .oe_form_header {
+ padding: 8px;
+}
+.openerp .oe_form_invalid input, .openerp .oe_form_invalid select, .openerp .oe_form_invalid textarea {
+ background-color: #ff6666 !important;
+ border: 1px solid #dd0000 !important;
+}
+.openerp .oe_form_button_save_dirty {
+ color: white;
+ background: #dc5f59;
+ background: -moz-linear-gradient(#dc5f59, #b33630);
+ background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
+ background: -webkit-linear-gradient(#dc5f59, #b33630);
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none;
+ font-weight: bold;
+}
+.openerp .oe_form_button_save_dirty:hover {
+ background: #ed6f6a;
+}
+.openerp .oe_form_topbar {
+ border-bottom: 1px solid #cacaca;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+ padding: 0 8px;
+ line-height: 30px;
+}
+.openerp .oe_form_topbar button {
+ font-size: 12px;
+ height: 24px;
+ line-height: 24px;
+ vertical-align: top;
+ padding: 0 10px;
+ margin: 3px 0;
+}
+.openerp .oe_form_topbar button span.i {
+ line-height: 20px;
+ height: 24px;
+}
+.openerp .oe_form_topbar ul {
+ height: 30px;
+ padding: 0;
+ margin: 0;
+ text-shadow: 0 1px 1px white;
+ border-left: 1px solid #cacaca;
+ border-right: 1px solid #cacaca;
+}
+.openerp .oe_form_topbar ul li {
+ padding: 0;
+ margin: 0;
+ float: left;
+ vertical-align: top;
+ border-right: 1px solid #cacaca;
+ height: 30px;
+ padding: 0 12px;
+}
+.openerp .oe_form_topbar ul li:first-child {
+ border-left: 1px solid #cacaca;
+}
+.openerp .oe_form_topbar ul li a {
+ color: #4c4c4c;
+}
+.openerp .oe_form_topbar ul li a:hover {
+ color: black;
+}
+.openerp .oe_form_topbar .oe_form_steps img {
+ margin: 0 8px;
+ vertical-align: top;
+}
+.openerp .oe_form_topbar .oe_form_steps li {
+ border-right: none;
+ padding: 0;
+}
+.openerp .oe_form_topbar .oe_form_steps li:first-child {
+ margin-left: 12px;
+ border-left: none;
+}
+.openerp .oe_form_topbar .oe_form_steps li:last-child {
+ margin-right: 12px;
+}
+.openerp .oe_form_topbar .oe_form_steps_active {
+ font-weight: bold;
+ color: #b33630;
+}
+.openerp .oe_application .oe_form_sheetbg {
+ background: url(/web/static/src/img/form_sheetbg.png);
+ padding: 8px;
+ border-bottom: 1px solid #dddddd;
+}
+.openerp .oe_application .oe_form_sheet_width {
+ min-width: 650px;
+ max-width: 980px;
+ margin: 0 auto;
+}
+.openerp .oe_application .oe_form_sheet {
+ padding: 8px 8px 8px 8px;
+ background: white;
+ min-height: 420px;
+ border: 1px solid #afafb6;
+ -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+ -box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+}
+.openerp .oe_form_autolayout .oe_button {
+ height: 22px;
+}
+.openerp .oe_form_autolayout .oe_form_group_cell {
+ line-height: 18px;
+ padding: 2px;
+ height: 25px;
+}
+.openerp .oe_form_autolayout .oe_form_group_cell .oe_form_button {
+ width: 100%;
+ white-space: nowrap;
+}
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_many2one,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_many2one input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_char input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_text textarea,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_email,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_email input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_url,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_url input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_date input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_float input,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_selection select,
+.openerp .oe_form_autolayout .oe_form_group_cell > .oe_datepicker_root {
+ width: 100%;
+}
+.openerp .oe_form_autolayout .oe_form_button.oe_button {
+ color: #4c4c4c;
+ white-space: nowrap;
+ min-width: 100%;
+ width: 100%;
+ height: 22px;
+}
+.openerp .oe_form_autolayout .oe_form_button.oe_button span {
+ position: relative;
+ vertical-align: top;
+}
+.openerp .oe_form_autolayout .oe_form_group_cell.oe_form_group_nested {
+ padding: 0;
+}
+.openerp .oe_form label {
+ font-weight: bold;
+}
+.openerp .oe_form_label_help[for], .openerp .oe_form_label[for] {
+ white-space: nowrap;
+}
+.openerp .oe_form textarea {
+ resize: vertical;
+}
+.openerp .oe_form input[type="text"],
+.openerp .oe_form input[type="password"],
+.openerp .oe_form input[type="file"],
+.openerp .oe_form select {
+ height: 22px;
+ padding-top: 2px;
+}
+.openerp .oe_form input[type="text"],
+.openerp .oe_form input[type="password"],
+.openerp .oe_form input[type="file"],
+.openerp .oe_form select,
+.openerp .oe_form textarea {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0 2px;
+ margin: 0 2px;
+ border: 1px solid #999999;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: white;
+ min-width: 90px;
+ color: #1f1f1f;
+}
+.openerp .oe_form input[readonly],
+.openerp .oe_form select[readonly],
+.openerp .oe_form textarea[readonly],
+.openerp .oe_form input[disabled],
+.openerp .oe_form select[disabled],
+.openerp .oe_form textarea[disabled] {
+ background: #e5e5e5 !important;
+ color: #666666;
+}
+.openerp .oe_form .oe_form_field_many2one input,
+.openerp .oe_form .oe_form_field_binary input,
+.openerp .oe_form .oe_form_field_binary input,
+.openerp .oe_form .oe_form_field_email input,
+.openerp .oe_form .oe_form_field_url input {
+ border-right: none;
+ -webkit-border-top-right-radius: 0px;
+ -webkit-border-bottom-right-radius: 0px;
+ -moz-border-radius-topright: 0px;
+ -moz-border-radius-bottomright: 0px;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+}
+.openerp .oe_form .oe_form_field_email button img,
+.openerp .oe_form .oe_form_field_url button img {
+ vertical-align: top;
+}
+.openerp .oe_form .oe_form_field_translatable,
+.openerp .oe_form .oe_form_field_many2one,
+.openerp .oe_form .oe_form_field_date,
+.openerp .oe_form .oe_form_field_datetime {
+ white-space: nowrap;
+}
+.openerp .oe_form .oe_form_field_boolean {
+ padding-top: 4px;
+}
+.openerp .oe_form .oe_form_field_datetime input {
+ min-width: 11em;
+}
+.openerp .oe_form .oe_datepicker_container {
+ display: none;
+}
+.openerp .oe_form .oe_datepicker_root {
+ display: inline-block;
+}
+.openerp .oe_form .oe_form_required input, .openerp .oe_form .oe_form_required select, .openerp .oe_form .oe_form_required textarea {
+ background-color: #d2d2ff !important;
+}
+.openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea {
+ background-color: #ff6666 !important;
+ border: 1px solid #dd0000 !important;
+}
+.openerp .oe_form .oe_button.oe_field_button {
+ -webkit-border-top-left-radius: 0px;
+ -webkit-border-bottom-left-radius: 0px;
+ -moz-border-radius-topleft: 0px;
+ -moz-border-radius-bottomleft: 0px;
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+ margin-right: -1px;
+ height: 22px;
+}
+.openerp .oe_form .oe_input_icon {
+ cursor: pointer;
+ margin: 3px 0 0 -21px;
+ vertical-align: top;
+}
+.openerp .oe_form .oe_input_icon_disabled {
+ position: absolute;
+ cursor: default;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ right: 5px;
+ top: 3px;
+}
+.openerp .oe_form .oe-binary-file-set {
+ overflow: hidden;
+ position: relative;
+ width: 45px;
+ height: 30px;
+}
+.openerp .oe_form input.oe-binary-file {
+ z-index: 0;
+ line-height: 0;
+ font-size: 12px;
+ position: absolute;
+ top: 1px;
+ right: 10px;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ -ms-filter: "alpha(opacity=0)";
+ margin: 0;
+ padding: 0;
+}
+.openerp .oe_horizontal_separator {
+ font-weight: bold;
+ border: 0 solid #666666;
+ border-bottom-width: 1px;
+ margin: 3px 4px 3px 1px;
+ height: 17px;
+}
+.openerp .oe_horizontal_separator:empty {
+ height: 5px;
+}
+.openerp .oe_vertical_separator {
+ border-left: 1px solid #666666;
+ padding: 0 4px 0 4px;
+}
+.openerp .oe_form_notebook {
+ margin: 8px 0;
+ padding: 0 8px;
+ list-style: none;
+ zoom: 1;
+}
+.openerp .oe_form_notebook:before, .openerp .oe_form_notebook:after {
+ display: table;
+ content: "";
+ zoom: 1;
+}
+.openerp .oe_form_notebook:after {
+ clear: both;
+}
+.openerp .oe_form_notebook > li {
+ float: left;
+}
+.openerp .oe_form_notebook > li > a {
+ display: block;
+ color: #4c4c4c;
+}
+.openerp .oe_form_notebook {
+ border-color: #dddddd;
+ border-style: solid;
+ border-width: 0 0 1px;
+}
+.openerp .oe_form_notebook > li {
+ position: relative;
+ margin-bottom: -1px;
+}
+.openerp .oe_form_notebook > li > a {
+ padding: 0 12px;
+ margin-right: 2px;
+ line-height: 30px;
+ border: 1px solid transparent;
+ -moz-border-radius: 4px 4px 0 0;
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+.openerp .oe_form_notebook > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.openerp .oe_form_notebook > li.ui-state-active > a, .openerp .oe_form_notebook > li.ui-state-active > a:hover {
+ background-color: white;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.openerp .ui-tabs-hide {
+ display: none;
+}
+.openerp .oe_form .oe_form_field_progressbar.ui-progressbar {
+ height: 22px;
+ font-size: 10px;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ border: 1px solid #999999;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: white;
+ min-width: 90px;
+}
+.openerp .oe_form .oe_form_field_progressbar.ui-progressbar span {
+ position: absolute;
+ margin-left: 10px;
+ font-weight: bold;
+}
+.openerp .oe_form .oe_form_field_progressbar.ui-progressbar .ui-widget-header {
+ background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
+}
+.openerp .oe_form .oe_view_manager_header2 td {
+ padding: 0px 8px;
+ line-height: 16px;
+}
+.openerp .oe_form .oe_view_manager_header2 td .oe_i {
+ font-size: 13px;
+}
+.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group {
+ height: auto;
+ line-height: 16px;
+}
+.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group li {
+ height: auto;
+ line-height: 16px;
+}
+.openerp .oe_listview_nocontent > img {
+ float: left;
+ margin-right: 1.5em;
+}
+.openerp .oe_listview_nocontent > div {
+ overflow: hidden;
+ padding: 6px;
+ font-size: 125%;
+}
+.openerp .oe-listview-content {
+ width: 100%;
+}
+.openerp .oe-listview-content thead, .openerp .oe-listview-content tfoot {
+ border-bottom: 1px solid #cacaca;
+ background: #eeeeee;
+}
+.openerp .oe-listview-content tfoot {
+ border-bottom: 1px solid #cacaca;
+ font-weight: bold;
+}
+.openerp .oe-listview-content tbody {
+ cursor: pointer;
+ background: white;
+}
+.openerp .oe-listview-content thead {
+ vertical-align: top;
+}
+.openerp .oe-listview-content td.oe_list_checkbox:first-child, .openerp .oe-listview-content th.oe_list_checkbox:first-child {
+ width: 17px;
+}
+.openerp .oe-listview-content td.oe_list_checkbox:first-child:after, .openerp .oe-listview-content th.oe_list_checkbox:first-child:after {
+ border-width: 0;
+}
+.openerp .oe-listview-content tbody tr:nth-child(odd) td, .openerp .oe-listview-content tbody tr:nth-child(odd) th {
+ background-color: #f0f0fa;
+ background-color: #f0f0fa;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
+ background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
+}
+.openerp .oe-listview-content tbody tr:hover td, .openerp .oe-listview-content tbody tr:hover th {
+ background-color: #eeeeee;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -moz-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -ms-linear-gradient(top, #eeeeee, #dedede);
+ background-image: -o-linear-gradient(top, #eeeeee, #dedede);
+ background-image: linear-gradient(to bottom, #eeeeee, #dedede);
+}
+.openerp .oe-listview-content td, .openerp .oe-listview-content th {
+ padding: 3px 6px;
+ line-height: 18px;
+}
+.openerp .oe-listview-content td button, .openerp .oe-listview-content th button {
+ border: none;
+ background: transparent;
+ padding: 0;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+}
+.openerp .oe-listview-content .numeric {
+ text-align: right;
+ width: 82px;
+}
+.openerp .oe-listview-content .numeric input {
+ text-align: right;
+}
+.openerp .oe-listview-content table .sortdown:after, .openerp .oe-listview-content table .sortdown:hover:after {
+ visibility: visible;
+ filter: alpha(opacity=60);
+ opacity: 0.6;
+}
+.openerp .oe-listview-content table .sortup:after {
+ border-bottom: none;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid black;
+ visibility: visible;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none;
+ filter: alpha(opacity=60);
+ opacity: 0.6;
+}
+.openerp .oe_kanban_group_title {
+ margin: 1px 1px 4px;
+ font-size: 15px;
+ font-weight: bold;
+ text-shadow: 0 1px 0 white;
+}
+.openerp .oe_kanban_column, .openerp .oe_kanban_group_header {
+ width: 240px;
+ vertical-align: top;
+ padding: 6px 7px 6px 6px;
+ background: #f0eeee;
+ border-left: 1px solid #f0f8f8;
+ border-right: 1px solid #b9b9b9;
+}
+.openerp .oe_kanban_record {
+ position: relative;
+ display: block;
+ min-height: 50px;
+ margin: 6px 0;
+ display: block;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+}
+.openerp .oe_kanban_record:last-child {
+ margin-bottom: 0;
+}
+.openerp .oe_kanban_record .oe_kanban_title {
+ font-weight: bold;
+ margin: 2px 4px;
+}
+.openerp .oe_kanban_gravatar {
+ width: 20px;
+ height: 20px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+.openerp .oe_kanban_avatar_toto {
+ height: 40px;
+ width: 40px;
+ border: 1px solid;
+ border-color: #e5e5e5 #dbdbdb #d2d2d2;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+.openerp .oe_trad_field.touched {
+ border: 1px solid green !important;
+}
+.openerp.oe_tooltip {
+ font-size: 12px;
+}
+.openerp.oe_tooltip .oe_tooltip_string {
+ color: #ffdd55;
+ font-weight: bold;
+ font-size: 13px;
+}
+.openerp.oe_tooltip .oe_tooltip_help {
+ white-space: pre-wrap;
+}
+.openerp.oe_tooltip .oe_tooltip_technical {
+ padding: 0 0 4px 0;
+ margin: 5px 0 0 15px;
+}
+.openerp.oe_tooltip .oe_tooltip_technical li {
+ list-style: circle;
+}
+.openerp.oe_tooltip .oe_tooltip_technical_title {
+ font-weight: bold;
+}
+.openerp .oe_layout_debugging .oe_form_group {
+ outline: 2px dashed red;
+}
+.openerp .oe_layout_debugging .oe_form_group_cell {
+ outline: 1px solid blue;
+}
+.openerp .oe_debug_view {
+ float: left;
+}
+.openerp .oe_debug_view_log {
+ font-size: 95%;
+}
+.openerp .oe_debug_view_log label {
+ display: block;
+ width: 49%;
+ text-align: right;
+ float: left;
+ font-weight: bold;
+ color: #000099;
+}
+.openerp .oe_debug_view_log span {
+ display: block;
+ width: 49%;
+ float: right;
+ color: #333333;
+}
+
+.openerp .oe_form_field_many2one td:first-child {
+ position: relative;
+}
+.openerp .oe_form_field_many2one span.oe-m2o-drop-down-button {
+ position: absolute;
+ top: 3px;
+ right: 2px;
}
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index c31f501c499..98caeafbb29 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -1,9 +1,21 @@
+@charset "utf-8"
+
// Variables {{{
+$section-title-color: #8786b7
+$facets-border: #afafb6
+$facets-border-selected: #a6a6fe
$colour4: #8a89ba
-//$colour4: #d14836
// }}}
-
// Mixins {{{
+@font-face
+ font-family: 'mnmliconsRegular'
+ src: url('/web/static/src/font/mnmliconsv21-webfont.eot') format('eot')
+ src: url('/web/static/src/font/mnmliconsv21-webfont.woff') format('woff')
+ src: url('/web/static/src/font/mnmliconsv21-webfont.ttf') format('truetype')
+ src: url('/web/static/src/font/mnmliconsv21-webfont.svg') format('svg') active
+ font-weight: normal
+ font-style: normal
+
@mixin vertical-gradient($startColor: #555, $endColor: #333)
background-color: $startColor
background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */
@@ -46,48 +58,295 @@ $colour4: #8a89ba
-webkit-background-clip: $clip
-moz-background-clip: $clip
background-clip: $clip
+
+@mixin unscrew-box
+ -moz-box-sizing: border-box
+ -webkit-box-sizing: border-box
+ box-sizing: border-box
+
// }}}
-.openerp2
- // Common styles {{{
+.openerp.openerp-web-client-container
+ height: 100%
+ position: relative
+
+.openerp
+ // Global style {{{
padding: 0
margin: 0
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
color: #4c4c4c
font-size: 13px
background: white
- position: relative
-
+ // }}}
+ // Tag reset {{{
a
text-decoration: none
+ table
+ padding: 0
+ font-size: 13px
+ border-collapse: collapse
+ thead
+ font-weight: bold
+ background-color: #f0f0f0
+ th
+ border-right: 1px dotted #afafb6
+ &:last-child
+ border-right: none
+ th, td
+ padding: 0
+ line-height: 18px
+ text-align: left
+ th
+ font-weight: bold
+ vertical-align: middle
+ td
+ vertical-align: top
+ .zebra tbody tr:nth-child(odd) td
+ background-color: #f0f0fa
+ @include vertical-gradient(#f0f0fa, #eeeef6)
+ .zebra tbody tr:hover td
+ @include vertical-gradient(#eee, #dedede)
+ .numeric
+ text-align: right
+ width: 82px
+ input
+ text-align: right
+ ul, li, ol
+ margin: 0
+ padding: 0
+ li
+ list-style-type: none
// }}}
+ // Generic classes {{{
+ .oe_i
+ font-family: "mnmliconsRegular" !important
+ font-size: 21px
+ font-weight: 300 !important
+ .oe_left
+ float: left
+ margin-right: 8px
+ .oe_right
+ float: right
+ margin-left: 8px
+ .oe_clear
+ clear: both
+ .oe_wait
+ cursor: wait
+ // }}}
+ // Button style {{{
+ a.button:link, a.button:visited, button, input[type='submit']
+ display: inline-block
+ border: 1px solid #ababab
+ color: #404040
+ margin: 0
+ padding: 4px 12px
+ font-size: 13px
+ text-align: center
+ @include vertical-gradient(#efefef, #d8d8d8)
+ @include radius(3px)
+ @include box-shadow((0 1px 2px rgba(0, 0, 0, .1), 0 1px 1px rgba(255, 255, 255, .8) inset))
+ text-shadow: 0 1px 1px rgba(255, 255, 255, .5)
+ -webkit-font-smoothing: antialiased
+ outline: none
- // WebClient {{{
- .oe_webclient
- position: absolute
- top: 0
- bottom: 0
- left: 0
- right: 0
+ a.button:hover, button:hover, input[type='submit']:hover
+ @include vertical-gradient(#f6f6f6, #e3e3e3)
+ cursor: pointer
- .oe_application
+ a.button:focus, button:focus, input[type='submit']:focus
+ border: 1px solid #80bfff
+ @include vertical-gradient(#f6f6f6, #e3e3e3)
+ @include box-shadow((0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, .8) inset))
+
+ a.button:active, a.button.active, button:active, button.active, input[type='submit']:active, input[type='submit'].active
+ background: #e3e3e3
+ background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f
+ background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f
+ background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f
+ @include box-shadow(none)
+
+ a.button.disabled, button:disabled, input[type='submit']:disabled
+ background: #efefef !important
+ border: 1px solid #d1d1d1 !important
+ @include box-shadow(none !important)
+ color: #aaa !important
+ cursor: default
+ text-shadow: 0 1px 1px #fff !important
+
+ button.primary
+ color: white
+ @include vertical-gradient(#dc5f59, #b33630)
+ @include box-shadow(none)
+
+ button.primary:hover
+ @include vertical-gradient(lighten(#dc5f59, 3%), lighten(#b33630, 3%))
+ @include box-shadow(0 0 1px rgba(0,0,0,0.2))
+
+ button.primary:active
+ @include vertical-gradient(#b33630, #dc5f59)
+ @include box-shadow(none)
+
+ button.following
+ color: white
+ width: 120px
+ @include vertical-gradient($colour4, darken($colour4, 3%))
+
+ button.unfollow
+ display: none
+ color: white
+ width: 120px
+ @include vertical-gradient(#dc5f59, #b33630)
+
+ // }}}
+ // Loading {{{
+ .oe_loading
+ display: none
+ z-index: 100
+ position: fixed
+ top: 0
+ right: 50%
+ padding: 4px 12px
+ background: #A61300
+ color: white
+ text-align: center
+ border: 1px solid #900
+ border-top: none
+ -moz-border-radius-bottomright: 8px
+ -moz-border-radius-bottomleft: 8px
+ border-bottom-right-radius: 8px
+ border-bottom-left-radius: 8px
+ // }}}
+ // Notification {{{
+ .oe_notification
+ z-index: 1050
+ // }}}
+ // Login {{{
+ .oe_login
+ background: url("/web/static/src/img/pattern.png") repeat
+ text-align: center
+ font-size: 14px
+ height: 100%
+ li
+ list-style-type: none
+ padding-bottom: 4px
+ button
+ float: right
+ display: inline-block
+ cursor: pointer
+ padding: 6px 16px
+ border: 1px solid #222
+ color: white
+ margin: 0
+ @include vertical-gradient(#b92020, #600606)
+ @include radius(4px)
+ @include box-shadow((0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset))
+ input, select
+ width: 252px
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial
+ border: 1px solid #999999
+ background: whitesmoke
+ @include box-shadow(inset 0 1px 4px rgba(0, 0, 0, 0.3))
+ @include radius(3px)
+ input
+ margin-bottom: 9px
+ padding: 5px 6px
+ select
+ padding: 1px
+ .oe_login_dbpane
+ position: fixed
+ top: 0
+ right: 8px
+ padding: 5px 10px
+ color: #eee
+ border: solid 1px #333
+ background: #1e1e1e
+ background: rgba(30,30,30,0.94)
+ @include radius(0 0 8px 8px)
+ input
+ padding: 2px 4px
+ margin: 4px 0
+ .oe_login_bottom
position: absolute
- top: 32px
- bottom: 0
- left: 206px
+ top: 50%
+ left: 0
right: 0
+ bottom: 0
+ text-shadow: 0 1px 1px #999999
+ @include vertical-gradient(#b41616, #600606)
+ .oe_login_pane
+ position: absolute
+ top: 50%
+ left: 50%
+ margin: -160px -166px
+ border: solid 1px #333333
+ background: #1e1e1e
+ background: rgba(30,30,30,0.94)
+ padding: 22px 32px
+ color: #eee
+ text-align: left
+ @include radius(8px)
+ @include box-shadow(0 0 18px rgba(0, 0, 0, 0.9))
+ h2
+ margin-top: 0
+ font-size: 18px
+ .oe_login_logo
+ position: absolute
+ top: -70px
+ left: 0
+ width: 100%
+ margin: 0 auto
+ text-align: center
+ .oe_login_footer
+ position: absolute
+ bottom: -40px
+ left: 0
+ width: 100%
+ text-align: center
+ a
+ color: #eee
+ margin: 0 8px
+ &:hover
+ text-decoration: underline
+ span
+ font-weight: bold
+ font-size: 16px
+ .oe_login_error_message
+ display: none
+ background-color: #b41616
+ color: #eee
+ padding: 14px 18px
+ margin-top: 15px
+ text-align: center
+ @include radius(4px)
+ @include box-shadow(0 1px 4px rgba(0, 0, 0, 0.8))
+ .oe_login_invalid
+ .oe_login_error_message
+ display: inline-block
// }}}
-
- // Fullscreen mode {{{
+ // DatabaseManager {{{
+ .oe_database_manager
+ background: #fff
+ color: #000
+ text-align: left
+ .oe_database_manager_menu
+ color: #000
+ // }}}
+ // WebClient {{{
+ .oe_webclient
+ width: 100%
+ height: 100%
+ border-spacing: 0px
+ // }}}
+ // WebClient.fullscreen {{{
.oe_content_full_screen
.oe_application
top: 0
left: 0
- .topbar, .leftbar
+ .oe_topbar, .oe_leftbar
display: none
- // }}
-
- // Topbar {{{
+ // }}}
+ // WebClient.topbar {{{
.oe_topbar
width: 100%
height: 31px
@@ -129,23 +388,28 @@ $colour4: #8a89ba
@include radius(4px)
.oe_topbar_avatar
vertical-align: top
+ .oe_dropdown_toggle:after
+ width: 0
+ height: 0
+ display: inline-block
+ content: "&darr"
+ text-indent: -99999px
+ vertical-align: top
+ margin-top: 8px
+ margin-left: 4px
+ border-left: 4px solid transparent
+ border-right: 4px solid transparent
+ border-top: 4px solid white
+ @include opacity(0.5)
// }}}
-
- // Leftbar {{{
+ // Webclient.leftbar {{{
.oe_leftbar
- width: 205px
- height: 100%
+ width: 220px
background: #f0eeee
border-right: 1px solid #afafb6
- overflow: auto
text-shadow: 0 1px 1px white
-
- .oe_footer
- position: absolute
- width: 205px
- text-align: center
- bottom: 8px
a.oe_logo
+ width: 220px
display: block
text-align: center
height: 70px
@@ -155,10 +419,12 @@ $colour4: #8a89ba
width: 157px
margin: 14px 0
.oe_footer
- position: absolute
- width: 205px
+ position: fixed
+ bottom: 0
+ padding: 4px 0
+ background: #f0eeee
+ width: 220px
text-align: center
- bottom: 8px
a
font-weight: 800
font-family: serif
@@ -168,14 +434,58 @@ $colour4: #8a89ba
color: #c81010
font-style: italic
// }}}
-
+ // UserMenu {{{
+ .oe_user_menu
+ float: right
+ padding: 0
+ margin: 0
+ li
+ list-style-type: none
+ float: left
+ .oe_dropdown
+ position: relative
+ .oe_dropdown_options
+ float: left
+ background: #333
+ background: rgba(37,37,37,0.9)
+ display: none
+ position: absolute
+ top: 32px
+ right: -1px
+ border: 0
+ z-index: 900
+ margin-left: 0
+ margin-right: 0
+ padding: 6px 0
+ zoom: 1
+ border-color: #999
+ border-color: rgba(0, 0, 0, 0.2)
+ border-style: solid
+ border-width: 0 1px 1px
+ @include radius(0 0 6px 6px)
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
+ @include background-clip()
+ li
+ float: none
+ display: block
+ background-color: none
+ a
+ display: block
+ padding: 4px 15px
+ clear: both
+ font-weight: normal
+ line-height: 18px
+ color: #eee
+ &:hover
+ @include vertical-gradient(#292929, #191919)
+ @include box-shadow(none)
+ // }}}
// Menu {{{
.oe_menu
float: left
padding: 0
margin: 0
li
- list-style-type: none
float: left
a
display: block
@@ -220,7 +530,6 @@ $colour4: #8a89ba
li
position: relative
padding: 1px 0 1px 16px
- list-style-type: none
a
display: block
color: #4c4c4c
@@ -238,6 +547,16 @@ $colour4: #8a89ba
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
@include radius(4px)
@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2))
+ .oe_menu_counter
+ float: right
+ background: #8a89ba
+ color: #eee
+ font-size: 12px
+ border: 1px solid lightgray
+ padding: 0px 4px 0px 4px
+ text-shadow: 0 1px 1px rgba(0,0,0,0.2)
+ margin: 0px
+ @include radius(4px)
.oe_active
background: $colour4
border-top: 1px solid lightGray
@@ -251,6 +570,9 @@ $colour4: #8a89ba
color: $colour4
text-shadow: 0 1px 1px white
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2))
+ .oe_menu_counter
+ background: #eee
+ color: #8a89ba
.oe_menu_toggler:before
width: 0
height: 0
@@ -273,11 +595,180 @@ $colour4: #8a89ba
border-right: 4px solid transparent
border-top: 4px solid #4c4c4c
// }}}
-
- // UserMenu {{{
- .oe_dropdown
+ // ActionManager {{{
+ .oe_application
+ width: 100%
+ a
+ color: $colour4
+ &:hover
+ text-decoration: underline
+ // }}}
+ // Content Header MIT {{{
+ .oe-view-manager-header
+ border-top: 1px solid #cacaca
+ border-bottom: 1px solid #cacaca
+ @include vertical-gradient(#fcfcfc, #dedede)
+ @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
+ h2
+ float: left
+ font-size: 18px
+ margin: 1px 0
+ a
+ color: $colour4
+ .buttons
+ padding-right: 8px
+ li
+ float: left
+ margin-right: 12px
+ .filter
+ vertical-align: top
+ li
+ height: 24px
+ line-height: 24px
+ padding: 0 8px
+ .header-row
+ height: 26px
+ line-height: 26px
+ margin: 8px
+ clear: both
+ text-shadow: 0 1px 1px white
+ .dropdown-toggle:after
+ margin-top: 7px
+ margin-left: 6px
+ border-top-color: #404040
+ .oe_vm_switch
+ li
+ padding-left: 1px
+ text-align: center
+ width: 24px
+ height: 24px
+ line-height: 24px
+ .pagination
+ li
+ height: 24px
+ line-height: 24px
+ padding: 0 8px
+ .button-group
+ display: inline-block
+ border: 1px solid #ababab
+ @include radius(5px)
+ li
+ float: left
+ border-right: 1px solid #ababab
+ &:last-child
+ border: none
+ a
+ color: #4c4c4c
+ &:hover
+ text-decoration: none
+ .active
+ width: 100%
+ background: #999
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
+ color: #fff
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ // }}}
+ // ViewManager.header {{{
+ .oe_view_manager_header2
+ width: 100%
+ border-top: 1px solid #cacaca
+ border-bottom: 1px solid #cacaca
+ @include vertical-gradient(#fcfcfc, #dedede)
+ @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
+ h2
+ float: left
+ font-size: 18px
+ margin: 1px 0
+ a
+ color: $colour4
+ td
+ padding: 4px 8px
+ line-height: 26px
+ .oe_button_group
+ display: inline-block
+ border: 1px solid #ababab
+ @include radius(5px)
+ li
+ float: left
+ border-right: 1px solid #ababab
+ &:last-child
+ border: none
+ a
+ color: #4c4c4c
+ &:hover
+ text-decoration: none
+ .active
+ background: #999
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
+ a
+ color: #fff
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ .oe_view_manager_buttons
+ white-space: nowrap
+ // }}}
+ // ViewManager.body {{{
+ .oe_view_manager_body
+ h4
+ margin: 8px 0
+ // }}}
+ // ViewManager.pager {{{
+ .oe_pager_value
+ float: left
+ margin-right: 8px
+ .oe_pager_group
+ float: left
+ height: 24px
+ line-height: 24px
+ display: inline-block
+ border: 1px solid #ababab
+ cursor: pointer
+ @include radius(5px)
+ li
+ height: 24px
+ line-height: 24px
+ padding: 0
+ float: left
+ border-right: 1px solid #ababab
+ &:last-child
+ border: none
+ a
+ color: #4c4c4c
+ padding: 0 8px
+ &:hover
+ text-decoration: none
+ .active
+ background: #999
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
+ a
+ color: #fff
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ // }}}
+ // ViewManager.switches {{{
+ .oe_view_manager_switch
+ li
+ padding-left: 1px
+ text-align: center
+ width: 24px
+ height: 24px
+ line-height: 24px
+ .oe_vm_switch_list:after, .oe_vm_switch_tree:after
+ content: "ö"
+ .oe_vm_switch_graph:after
+ content: "ó"
+ .oe_vm_switch_gantt:after
+ content: "y"
+ .oe_vm_switch_calendar:after
+ content: "b"
+ .oe_vm_switch_kanban:after
+ content: "ó"
+ .oe_vm_switch_diagram:after
+ content: "}"
+ // }}}
+ // ViewManager.sidebar {{{
+ .oe_form_dropdown_section
position: relative
-
+ display: inline-block
+ margin-right: 4px
.oe_dropdown_toggle:after
width: 0
height: 0
@@ -285,61 +776,752 @@ $colour4: #8a89ba
content: "&darr"
text-indent: -99999px
vertical-align: top
- margin-top: 8px
- margin-left: 4px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid white
@include opacity(0.5)
-
- .oe_user_menu
- float: right
- padding: 0
- margin: 0
+ margin-top: 7px
+ margin-left: 6px
+ border-top-color: #404040
+ .oe_dropdown_menu
+ display: none
+ position: absolute
+ top: 28px
+ left: 0px
+ padding: 8px
+ border: 1px solid #afafb6
+ width: 120px
+ overflow-x: hidden
+ z-index: 900
+ text-align: left
+ background: white
+ @include radius(3px)
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
li
list-style-type: none
- float: left
+ float: none
+ display: block
+ background-color: none
+ a
+ display: block
+ padding: 3px 6px
+ clear: both
+ font-weight: normal
+ line-height: 14px
+ color: #4c4c4c
+ text-decoration: none
+ &:hover
+ text-decoration: none
+ @include vertical-gradient(#f0f0fa, #eeeef6)
+ @include box-shadow(none)
+ .oe_sidebar
+ white-space: nowrap
+ // }}}
+ // SearchView xmo {{{
+ .oe_searchview
+ position: relative
+ float: right
+ .VS-search
+ .VS-search-box
+ min-height: 0
+ border-width: 1px
+ padding: 0
+ width: 410px
+ //border: 1px solid #ababab
+ //margin-top: 0
+ //margin-bottom: 0
+ //padding: 4px 19px
+ //font-size: 13px
+ //background: url('../img/search.png') no-repeat 5px
+ //background-color: white
+ //@include radius(13px)
+ //@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset)
+ .VS-icon
+ top: 5px
- .oe_dropdown_options
- float: left
- background: #333
- background: rgba(37,37,37,0.9)
+ .VS-icon-search
+ left: 5px
+
+ .VS-search-inner
+ margin: 0 40px 0 17px
+ font-size: 13px
+
+ input
+ font-size: inherit
+ line-height: inherit
+ height: auto
+ padding: 0
+
+ .VS-input-width-tester
+ font-size: inherit
+ padding: 0
+
+ .search_input
+ margin-left: 0
+ height: 22px
+ input, .VS-input-width-tester
+ padding: 0 2px
+ height: inherit
+ margin: 0
+
+ .search_facet
+ padding: 0
+ margin: 1px 0
+ border: 1px solid $facets-border
+ @include radius(3px)
+ background: #f0f0fa
+ height: 18px
+
+ &.is_selected
+ border-color: $facets-border-selected
+ @include box-shadow(0 0 3px 1px $facets-border-selected)
+
+ .category
+ margin-left: 0
+ .VS-icon-cancel
+ // don't change the icon on selection
+ @include opacity(1.0)
+ background-position: center 0
+
+ .category
+ padding: 0 4px 0 14px
+ color: white
+ background: #8786b7
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
+ font-weight: normal
+ text-transform: none
+ height: 18px
+ line-height: 18px
+ font-size: inherit
+
+ .search_facet_input_container
+ border-left: 1px solid $facets-border
+ height: 18px
+ line-height: 18px
+ padding: 0 4px
+ cursor: pointer
+
+ .search_facet_remove
+ left: 1px
+ top: 3px
+
+ .VS-icon-cancel
+ right: 24px
+
+ .oe_vs_unfold_drawer
+ position: absolute
+ top: 0
+ right: 0
+ height: 100%
+ line-height: 23px
+ padding: 0 7px 0 4px
+ color: #ccc
+ cursor: pointer
+ &:before
+ content: "▾"
+
+ &.oe_searchview_open_drawer
+ .oe_searchview_drawer
+ display: block
+
+ .oe_searchview_drawer
+ position: absolute
+ z-index: 1
+ top: 100%
+ right: 0
+ background-color: white
+ width: 410px
+ display: none
+ border: 1px solid #ccc
+ text-align: left
+
+ .oe_searchview_filters
+ display: table
+ width: 100%
+
+ > div
+ @include unscrew-box
+ display: table-cell
+ width: 50%
+
+ h3
+ margin: 2px 4px 2px 8px
+ color: $section-title-color
+
+ ul
+ margin: 0 12px 3px
+ padding: 0
+ list-style: none
+
+ li
+ list-style: none
+ padding: 3px 6px
+ height: 14px
+ color: inherit
+ cursor: pointer
+
+ &:hover
+ background-color: #f0f0fa
+
+ .oe_searchview_advanced
+ h4
+ border-top: 1px solid #ccc
+ margin: 5px 0 3px
+ cursor: pointer
+
+ h4:before
+ content: "▸ "
+
+ div
+ display: none
+
+ button.oe_add_condition:before
+ content: "⊞ "
+ button.oe_apply:before
+ content: "⚡ "
+
+ ul
+ list-style: none
+ padding: 0
+ li
+ list-style: none
+ margin: 0
+
+ &.oe_opened
+ h4:before
+ content: "▾ "
+ div
+ display: block
+ // }}}
+ // View.topbar {{{
+ .oe_view_topbar
+ border-bottom: 1px solid #cacaca
+ @include vertical-gradient(#fcfcfc, #dedede)
+ padding: 0 8px
+ line-height: 30px
+ // }}}
+ // FormView.body {{{
+ .oe_formview
+ background: white
+ .oe_form_header
+ padding: 8px
+ .oe_form_invalid
+ input, select, textarea
+ background-color: #F66 !important
+ border: 1px solid #D00 !important
+ .oe_form_button_save_dirty
+ color: white
+ background: #dc5f59
+ background: -moz-linear-gradient(#dc5f59, #b33630)
+ background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630))
+ background: -webkit-linear-gradient(#dc5f59, #b33630)
+ -moz-box-shadow: none
+ -webkit-box-shadow: none
+ -box-shadow: none
+ font-weight: bold
+ &:hover
+ background: #ED6F6A
+ // }}}
+ // FormView.customdivs {{{
+ .oe_form_topbar
+ border-bottom: 1px solid #cacaca
+ @include vertical-gradient(#fcfcfc, #dedede)
+ padding: 0 8px
+ line-height: 30px
+ button
+ font-size: 12px
+ height: 24px
+ line-height: 24px
+ vertical-align: top
+ padding: 0 10px
+ margin: 3px 0
+ span.i
+ line-height: 20px
+ height: 24px
+ ul
+ height: 30px
+ padding: 0
+ margin: 0
+ text-shadow: 0 1px 1px white
+ border-left: 1px solid #cacaca
+ border-right: 1px solid #cacaca
+ li
+ padding: 0
+ margin: 0
+ float: left
+ vertical-align: top
+ border-right: 1px solid #cacaca
+ height: 30px
+ padding: 0 12px
+ &:first-child
+ border-left: 1px solid #cacaca
+ a
+ color: #4c4c4c
+ &:hover
+ color: black
+ .oe_form_steps
+ img
+ margin: 0 8px
+ vertical-align: top
+ li
+ border-right: none
+ padding: 0
+ &:first-child
+ margin-left: 12px
+ border-left: none
+ &:last-child
+ margin-right: 12px
+ .oe_form_steps_active
+ font-weight: bold
+ color: #b33630
+ .oe_application
+ .oe_form_sheetbg
+ background: url(/web/static/src/img/form_sheetbg.png)
+ padding: 8px
+ border-bottom: 1px solid #ddd
+ .oe_form_sheet_width
+ min-width: 650px
+ max-width: 980px
+ margin: 0 auto
+ .oe_form_sheet
+ padding: 8px 8px 8px 8px
+ background: white
+ min-height: 420px
+ border: 1px solid #afafb6
+ @include box-shadow(0 0 10px rgba(0,0,0,0.3))
+ //.avatar
+ // max-height: 52px
+ //.contacts
+ // li
+ // float: left
+ // width: 33%
+ // margin-bottom: 4px
+ // .avatar
+ // height: 40px
+ // width: 40px
+ // border: 1px solid
+ // border-color: #e5e5e5 #dbdbdb #d2d2d2
+ // .data
+ // padding: 2px
+ // line-height: 18px
+ // }}}
+ // FormView.autolayout {{{
+ .oe_form_autolayout
+ .oe_button
+ height: 22px
+ .oe_form_group_cell
+ line-height: 18px
+ padding: 2px
+ height: 25px
+ .oe_form_button
+ width: 100%
+ white-space: nowrap
+ .oe_form_group_cell > .oe_form_field_many2one,
+ .oe_form_group_cell > .oe_form_field_many2one input,
+ .oe_form_group_cell > .oe_form_field_char input,
+ .oe_form_group_cell > .oe_form_field_text textarea,
+ .oe_form_group_cell > .oe_form_field_email,
+ .oe_form_group_cell > .oe_form_field_email input,
+ .oe_form_group_cell > .oe_form_field_url,
+ .oe_form_group_cell > .oe_form_field_url input,
+ .oe_form_group_cell > .oe_form_field_date input,
+ .oe_form_group_cell > .oe_form_field_float input,
+ .oe_form_group_cell > .oe_form_field_selection select,
+ .oe_form_group_cell > .oe_datepicker_root
+ width: 100%
+ .oe_form_button.oe_button
+ color: #4c4c4c
+ white-space: nowrap
+ min-width: 100%
+ width: 100%
+ height: 22px
+ span
+ position: relative
+ vertical-align: top
+ .oe_form_group_cell.oe_form_group_nested
+ padding: 0
+ // }}}
+ // FormView.label {{{
+ .oe_form
+ label
+ font-weight: bold
+ .oe_form_label_help[for], .oe_form_label[for]
+ white-space: nowrap
+ // }}}
+ // FormView.fields {{{
+ .oe_form
+ textarea
+ resize: vertical
+ input[type="text"],
+ input[type="password"],
+ input[type="file"],
+ select
+ height: 22px
+ padding-top: 2px
+ input[type="text"],
+ input[type="password"],
+ input[type="file"],
+ select,
+ textarea
+ -moz-box-sizing: border-box
+ -webkit-box-sizing: border-box
+ -ms-box-sizing: border-box
+ box-sizing: border-box
+ padding: 0 2px
+ margin: 0 2px
+ border: 1px solid #999
+ -moz-border-radius: 3px
+ -webkit-border-radius: 3px
+ border-radius: 3px
+ background: white
+ min-width: 90px
+ color: #1f1f1f
+ input[readonly],
+ select[readonly],
+ textarea[readonly],
+ input[disabled],
+ select[disabled],
+ textarea[disabled]
+ background: #E5E5E5 !important
+ color: #666
+ .oe_form_field_many2one input,
+ .oe_form_field_binary input,
+ .oe_form_field_binary input,
+ .oe_form_field_email input,
+ .oe_form_field_url input
+ border-right: none
+ -webkit-border-top-right-radius: 0px
+ -webkit-border-bottom-right-radius: 0px
+ -moz-border-radius-topright: 0px
+ -moz-border-radius-bottomright: 0px
+ border-top-right-radius: 0px
+ border-bottom-right-radius: 0px
+ .oe_form_field_email button img,
+ .oe_form_field_url button img
+ vertical-align: top
+ .oe_form_field_translatable,
+ .oe_form_field_many2one,
+ .oe_form_field_date,
+ .oe_form_field_datetime
+ white-space: nowrap
+ .oe_form_field_boolean
+ padding-top: 4px
+ .oe_form_field_datetime input
+ min-width: 11em
+ .oe_datepicker_container
display: none
+ .oe_datepicker_root
+ display: inline-block
+ .oe_form_required
+ input, select, textarea
+ background-color: #D2D2FF !important
+ .oe_form_invalid
+ input, select, textarea
+ background-color: #F66 !important
+ border: 1px solid #D00 !important
+ // }}}
+ // FormView.buttons {{{
+ .oe_form
+ .oe_button.oe_field_button
+ -webkit-border-top-left-radius: 0px
+ -webkit-border-bottom-left-radius: 0px
+ -moz-border-radius-topleft: 0px
+ -moz-border-radius-bottomleft: 0px
+ border-top-left-radius: 0px
+ border-bottom-left-radius: 0px
+ margin-right: -1px
+ height: 22px
+ .oe_input_icon
+ cursor: pointer
+ margin: 3px 0 0 -21px
+ vertical-align: top
+ .oe_input_icon_disabled
position: absolute
- top: 32px
- right: -1px
- border: 0
- z-index: 900
- margin-left: 0
- margin-right: 0
- padding: 6px 0
- zoom: 1
- border-color: #999
- border-color: rgba(0, 0, 0, 0.2)
- border-style: solid
- border-width: 0 1px 1px
- @include radius(0 0 6px 6px)
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
- @include background-clip()
+ cursor: default
+ opacity: 0.5
+ filter: alpha(opacity=50)
+ right: 5px
+ top: 3px
+ // }}}
+ // FormView.binaryfile {{{
+ /* http://www.quirksmode.org/dom/inputfile.html
+ * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
+ */
+ .oe_form
+ .oe-binary-file-set
+ overflow: hidden
+ position: relative
+ width: 45px
+ height: 30px
+ input.oe-binary-file
+ z-index: 0
+ line-height: 0
+ font-size: 12px
+ position: absolute
+ // Should be adjusted for all browsers
+ top: 1px
+ right: 10px
+ opacity: 0
+ filter: alpha(opacity = 0)
+ -ms-filter: "alpha(opacity=0)"
+ margin: 0
+ padding: 0
+ // }}}
+ // FormView.separator {{{
+ .oe_horizontal_separator
+ font-weight: bold
+ border: 0 solid #666
+ border-bottom-width: 1px
+ margin: 3px 4px 3px 1px
+ height: 17px
+ .oe_horizontal_separator:empty
+ height: 5px
+ .oe_vertical_separator
+ border-left: 1px solid #666
+ padding: 0 4px 0 4px
+ // }}}
+ // FormView.notebook {{{
+ .oe_form_notebook
+ margin: 8px 0
+ padding: 0 8px
+ list-style: none
+ zoom: 1
+ .oe_form_notebook:before, .oe_form_notebook:after
+ display: table
+ content: ""
+ zoom: 1
+ .oe_form_notebook:after
+ clear: both
+ .oe_form_notebook > li
+ float: left
+ .oe_form_notebook > li > a
+ display: block
+ color: #4c4c4c
+ .oe_form_notebook
+ border-color: #ddd
+ border-style: solid
+ border-width: 0 0 1px
+ .oe_form_notebook > li
+ position: relative
+ margin-bottom: -1px
+ .oe_form_notebook > li > a
+ padding: 0 12px
+ margin-right: 2px
+ line-height: 30px
+ border: 1px solid transparent
+ @include radius(4px 4px 0 0)
+ .oe_form_notebook > li > a:hover
+ text-decoration: none
+ background-color: #eee
+ border-color: #eee #eee #ddd
+ .oe_form_notebook > li.ui-state-active > a, .oe_form_notebook > li.ui-state-active > a:hover
+ background-color: #ffffff
+ border: 1px solid #ddd
+ border-bottom-color: transparent
+ cursor: default
+ .ui-tabs-hide
+ display: none
+
+ // }}}
+ // FormView.progressbar {{{
+ .oe_form
+ .oe_form_field_progressbar.ui-progressbar
+ height: 22px
+ font-size: 10px
+ -moz-box-sizing: border-box
+ -webkit-box-sizing: border-box
+ -ms-box-sizing: border-box
+ box-sizing: border-box
+ border: 1px solid #999
+ -moz-border-radius: 3px
+ -webkit-border-radius: 3px
+ border-radius: 3px
+ background: white
+ min-width: 90px
+
+ span
+ position: absolute
+ margin-left: 10px
+ font-weight: bold
+
+ .ui-widget-header
+ background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x
+ // }}}
+ // FormView.viewmanager {{{
+ .oe_form
+ .oe_view_manager_header2
+ td
+ padding: 0px 8px
+ line-height: 16px
+ .oe_i
+ font-size: 13px
+ .oe_pager_group
+ height: auto
+ line-height: 16px
+ li
+ height: auto
+ line-height: 16px
+
+ // }}}
+ // ListView {{{
+ .oe_listview_nocontent
+ > img
+ float: left
+ margin-right: 1.5em
+ > div
+ // don't encroach on my arrow
+ overflow: hidden
+ padding: 6px
+ font-size: 125%
+ .oe-listview-content
+ width: 100%
+ thead, tfoot
+ border-bottom: 1px solid #cacaca
+ background: #eee
+ tfoot
+ border-bottom: 1px solid #cacaca
+ font-weight: bold
+ tbody
+ cursor: pointer
+ background: white
+ thead
+ vertical-align: top
+ td.oe_list_checkbox:first-child, th.oe_list_checkbox:first-child
+ width: 17px
+ &:after
+ border-width: 0
+ tbody tr:nth-child(odd) td, tbody tr:nth-child(odd) th
+ background-color: #f0f0fa
+ @include vertical-gradient(#f0f0fa, #eeeef6)
+ tbody tr:hover td, tbody tr:hover th
+ @include vertical-gradient(#eee, #dedede)
+ td, th
+ padding: 3px 6px
+ line-height: 18px
+ button
+ border: none
+ background: transparent
+ padding: 0
+ @include box-shadow(none)
+ .numeric
+ text-align: right
+ width: 82px
+ input
+ text-align: right
+ //thead th:after
+ //content: ""
+ //float: right
+ //margin-top: 7px
+ //border-width: 0 4px 4px
+ //border-style: solid
+ //border-color: #000 transparent
+ //visibility: hidden
+ table .sortdown:after, table .sortdown:hover:after
+ visibility: visible
+ @include opacity(0.6)
+ table .sortup:after
+ border-bottom: none
+ border-left: 4px solid transparent
+ border-right: 4px solid transparent
+ border-top: 4px solid #000
+ visibility: visible
+ @include box-shadow(none)
+ @include opacity(0.6)
+ // }}}
+ // KanbanView {{{
+ .oe_kanban_group_title
+ margin: 1px 1px 4px
+ font-size: 15px
+ font-weight: bold
+ text-shadow: 0 1px 0 white
+ .oe_kanban_column, .oe_kanban_group_header
+ width: 240px
+ vertical-align: top
+ padding: 6px 7px 6px 6px
+ background: #f0eeee
+ border-left: 1px solid #f0f8f8
+ border-right: 1px solid #b9b9b9
+ .oe_kanban_record
+ position: relative
+ display: block
+ min-height: 50px
+ margin: 6px 0
+ display: block
+ @include radius(4px)
+ &:last-child
+ margin-bottom: 0
+ .oe_kanban_title
+ font-weight: bold
+ margin: 2px 4px
+ .oe_kanban_gravatar
+ width: 20px
+ height: 20px
+ @include radius(3px)
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.2))
+ .oe_kanban_avatar_toto
+ height: 40px
+ width: 40px
+ border: 1px solid
+ border-color: #e5e5e5 #dbdbdb #d2d2d2
+ @include radius(3px)
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.2))
+ // }}}
+ // Translation {{{
+ .oe_trad_field.touched
+ border: 1px solid green !important
+ // }}}
+ // Tooltips {{{
+ &.oe_tooltip
+ font-size: 12px
+ .oe_tooltip_string
+ color: #FD5
+ font-weight: bold
+ font-size: 13px
+ .oe_tooltip_help
+ white-space: pre-wrap
+ .oe_tooltip_technical
+ padding: 0 0 4px 0
+ margin: 5px 0 0 15px
li
- float: none
- display: block
- background-color: none
- a
- display: block
- padding: 4px 15px
- clear: both
- font-weight: normal
- line-height: 18px
- color: #eee
- &:hover
- @include vertical-gradient(#292929, #191919)
- @include box-shadow(none)
+ list-style: circle
+ .oe_tooltip_technical_title
+ font-weight: bold
// }}}
+ // Debugging stuff {{{
+ .oe_layout_debugging
+ .oe_form_group
+ outline: 2px dashed red
+ .oe_form_group_cell
+ outline: 1px solid blue
-.openerp
- // Transitional overrides for old styles {{{
+ .oe_debug_view
+ float: left
+ .oe_debug_view_log
+ font-size: 95%
+ .oe_debug_view_log label
+ display: block
+ width: 49%
+ text-align: right
+ float: left
+ font-weight: bold
+ color: #009
+ .oe_debug_view_log span
+ display: block
+ width: 49%
+ float: right
+ color: #333
// }}}
+// Transitional overrides for old styles {{{
+.openerp
+ .oe_form_field_many2one
+ td:first-child
+ position: relative
+ span.oe-m2o-drop-down-button
+ position: absolute
+ top: 3px
+ right: 2px
+// }}}
+
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
diff --git a/addons/web/static/src/css/base_old.css b/addons/web/static/src/css/base_old.css
index 2b6f6fbf639..8f333fae761 100644
--- a/addons/web/static/src/css/base_old.css
+++ b/addons/web/static/src/css/base_old.css
@@ -1,7 +1,6 @@
.openerp {
padding: 0;
margin: 0;
- height: 100%;
font-size: 80%;
font-family: Ubuntu, Helvetica, sans-serif;
}
@@ -14,10 +13,9 @@
.openerp .view-manager-main-content {
width: 100%;
- padding: 0 8px 8px 8px;
}
-.openerp .oe_form_frame_cell .view-manager-main-content {
+.openerp .oe_form_group_cell .view-manager-main-content {
padding: 0;
}
@@ -56,216 +54,6 @@
display: none;
}
-/* Loading */
-.loading {
- cursor: wait;
-}
-.openerp .loading {
- display: none;
- z-index: 100;
- position: fixed;
- top: 0;
- right: 50%;
- padding: 4px 12px;
- background: #A61300;
- color: white;
- text-align: center;
- border: 1px solid #900;
- border-top: none;
- -moz-border-radius-bottomright: 8px;
- -moz-border-radius-bottomleft: 8px;
- border-bottom-right-radius: 8px;
- border-bottom-left-radius: 8px;
-}
-.openerp .oe_notification {
- z-index: 1050;
- display: none;
-}
-.openerp .oe_notification * {
- color: white;
-}
-
-/* Login page */
-
-.login {
- padding: 0;
- margin: 0;
- font-family: "Lucida Grande", Helvetica, Verdana, Arial;
- background: url("/web/static/src/img/pattern.png") repeat;
- color: #eee;
- font-size: 14px;
- height: 100%;
-}
-
-.login ul, ol {
- padding: 0;
- margin: 0;
-}
-
-.login li {
- list-style-type: none;
- padding-bottom: 4px;
-}
-
-.login a {
- color: #eee;
- text-decoration: none;
-}
-
-.login button {
- float: right;
- display: inline-block;
- cursor: pointer;
- padding: 6px 16px;
- font-size: 13px;
- font-family: "Lucida Grande", Helvetica, Verdana, Arial;
- border: 1px solid #222222;
- color: white;
- margin: 0;
- background: #600606;
- background: -moz-linear-gradient(#b92020, #600606);
- background: -webkit-gradient(linear, left top, left bottom, from(#b92020), to(#600606));
- background: -ms-linear-gradient(top, #b92020, #600606);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b92020', endColorstr='#600606',GradientType=0 );
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(155, 155, 155, 0.4) inset;
- -o-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
-}
-
-.login input, .login select {
- width: 252px;
- font-size: 14px;
- font-family: "Lucida Grande", Helvetica, Verdana, Arial;
- border: 1px solid #999999;
- background: whitesmoke;
- -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-
-.login input {
- margin-bottom: 9px;
- padding: 5px 6px;
-}
-
-.login select {
- padding: 1px;
-}
-
-.login .dbpane {
- position: fixed;
- top: 0;
- right: 8px;
- padding: 5px 10px;
- color: #eee;
- border: solid 1px #333;
- background: #1e1e1e;
- background: rgba(30,30,30,0.94);
- -moz-border-radius: 0 0 8px 8px;
- -webkit-border-radius: 0 0 8px 8px;
- border-radius: 0 0 8px 8px;
-}
-
-.login .bottom {
- position: absolute;
- top: 50%;
- left: 0;
- right: 0;
- bottom: 0;
- text-shadow: 0 1px 1px #999999;
- background: #600606;
- background: -moz-linear-gradient(#b41616, #600606);
- background: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606));
- background: -ms-linear-gradient(top, #b41616, #600606);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b41616', endColorstr='#600606',GradientType=0 );
-}
-
-.login .pane {
- position: absolute;
- top: 50%;
- left: 50%;
- margin: -160px -166px;
- border: solid 1px #333333;
- background: #1e1e1e;
- background: rgba(30,30,30,0.94);
- padding: 22px 32px;
- text-align: left;
- -moz-border-radius: 8px;
- -webkit-border-radius: 8px;
- border-radius: 8px;
- -moz-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
- -webkit-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
- -box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
-}
-
-.login .pane h2 {
- margin-top: 0;
- font-size: 18px;
-}
-
-.login #logo {
- position: absolute;
- top: -70px;
- left: 0;
- width: 100%;
- margin: 0 auto;
- text-align: center;
-}
-
-.login .footer {
- position: absolute;
- bottom: -40px;
- left: 0;
- width: 100%;
- text-align: center;
-}
-
-.login .footer a {
- font-size: 13px;
- margin: 0 8px;
-}
-
-.login .footer a:hover {
- text-decoration: underline;
-}
-
-.login .openerp {
- font-weight: bold;
- font-family: serif;
- font-size: 16px;
-}
-
-.openerp .login {
- text-align: center;
-}
-
-.openerp .login .login_error_message {
- display: none;
- background-color: #b41616;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
- color: #eee;
- font-size: 14px;
- padding: 14px 18px;
- margin-top: 15px;
- text-align: center;
-}
-
-.openerp .login.login_invalid .login_error_message {
- display: inline-block;
-}
-
/* Database */
@@ -404,201 +192,6 @@ label.error {
padding-bottom:4px;
}
-/* View Manager */
-.openerp .oe_vm_switch {
- float: right;
-}
-.openerp .oe-view-manager-header .oe_view_title {
- font-size:150%;
- padding:2px 0 0 0;
-}
-
-/* SearchView */
-.openerp .oe_searchview_field > div {
- position: relative;
- white-space: nowrap;
-}
-.openerp .oe_searchview_field .oe_input_icon {
- top: auto;
- bottom: 3px;
-}
-
-.openerp .filter_label, .openerp .filter_icon {
- background: #F0F0F0;
- border: 1px solid #999;
- background: -moz-linear-gradient(top, #F0F0F0 0%, #C0C0C0 100%);
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F0F0F0), color-stop(100%,#C0C0C0));
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#C0C0C0',GradientType=0 );
-}
-.openerp .filter_label:hover, .openerp .filter_icon:hover {
- background: #F0F0F0;
- background: -moz-linear-gradient(top, #F0F0F0 0%, #A1A7CE 100%);
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F0F0F0), color-stop(100%,#A1A7CE));
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#A1A7CE',GradientType=0 );
-}
-.openerp .filter_label:active, .openerp .filter_icon:active {
- background: #aaa;
- background: -moz-linear-gradient(top, #999999 0%, #EEEEEE 100%);
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#999999), color-stop(100%,#EEEEEE));
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#999999', endColorstr='#EEEEEE',GradientType=0 );
-}
-.openerp .filter_label.enabled, .openerp .filter_icon.enabled {
- background: #aaa;
- filter: none;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none;
-}
-.openerp .filter_icon {
- height: 22px;
- padding: 1px 2px 0 2px;
- margin: 0;
- vertical-align: bottom;
-}
-.openerp .filter_label {
- font-weight: bold;
- text-transform: uppercase;
- text-shadow: #EEE 0 1px 0;
- color: #4C4C4C;
- white-space: nowrap;
- min-height: 40px;
- min-width: 75px;
- padding: 2px 4px;
- margin: 0;
-}
-.openerp .filter_label_group {
- padding-right: 0.4em;
- white-space: nowrap;
-}
-
-.openerp .filter_label_group button {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
- border-right: none;
-}
-.openerp .filter_label_group button:first-child {
- -webkit-border-top-left-radius: 7px;
- -webkit-border-bottom-left-radius: 7px;
- -moz-border-radius-topleft: 7px;
- -moz-border-radius-bottomleft: 7px;
- border-top-left-radius: 7px;
- border-bottom-left-radius: 7px;
- border-right: none;
-}
-.openerp .filter_label_group button:last-child {
- -webkit-border-top-right-radius: 7px;
- -webkit-border-bottom-right-radius: 7px;
- -moz-border-radius-topright: 7px;
- -moz-border-radius-bottomright: 7px;
- border-top-right-radius: 7px;
- border-bottom-right-radius: 7px;
- border-right: 1px solid #999;
-}
-.openerp .filter_label_group button.filter_icon img {
- padding: 1px 8px 0 8px;
-}
-.openerp .filter_label_group button.filter_icon:first-child {
- border-left: solid 1px #999;
- margin-left: -7px;
- -webkit-border-top-left-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -moz-border-radius-topleft: 0;
- -moz-border-radius-bottomleft: 0;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-}
-
-.openerp .searchview_group_string {
- display: block;
- color: #7D7979;
- font-weight: bold;
- padding: 2px 0 2px 10px;
- text-decoration: none;
-}
-.openerp .searchview_group_string:hover {
- background-color: #ccc;
-}
-.openerp .searchview_group.folded .searchview_group_string {
- background: url("/web/static/src/img/ui/group-folded.png") no-repeat scroll 0 50%;
-}
-.openerp .searchview_group.folded .searchview_group_content {
- display: none;
-}
-.openerp .searchview_group.expanded .searchview_group_string {
- background: url("/web/static/src/img/ui/group-expanded.png") no-repeat scroll 0 50%;
-}
-.openerp .searchview_group.expanded .searchview_group_content {
- display: block;
- padding-bottom:3px;
-}
-
-.openerp .searchview_group_content .oe_label, .openerp .searchview_group_content .oe_label_help {
- font-weight: bold;
- color: #4c4c4c;
-}
-
-.openerp .oe-searchview-render-line .oe_label, .openerp .oe-searchview-render-line .oe_label_help {
- font-weight: bold;
- font-size: 80%;
- white-space: nowrap;
-}
-
-.openerp .searchview_extended_group {
- padding: 3px;
- margin: 2px;
-}
-
-.openerp .searchview_extended_group .oe_adv_filters_and {
- border-bottom: 1px solid #8E8E8E;
- text-align: center;
- margin-top: -10px;
-}
-.openerp .searchview_extended_group .oe_adv_filters_and span {
- background: #F0EEEE;
- position: relative;
- top: 0.5em;
- padding: 0 1em 0 1em;
- color: #8E8E8E;
-}
-
-.openerp .searchview_extended_group.last_group .oe_adv_filters_and {
- display: none;
-}
-
-.openerp .oe_search-view-buttons {
- padding: 2px 0 10px 0;
- vertical-align:middle;
-}
-.openerp .oe_search-view-filters-management {
- float: right;
-}
-.openerp .oe_search-view-filters-management, .openerp .oe_search-view-custom-filter-btn {
- float:right;
-}
-
-.openerp .searchview_extended_add_proposition span {
- font-size: 0.9em;
- background: url(/web/static/src/img/icons/gtk-add.png) repeat-y;
- padding-left: 18px;
-}
-
-.openerp .searchview_extended_delete_group {
- float:right;
- display: none;
-}
-
-.openerp .searchview_extended_delete_prop {
- text-decoration: none;
-}
-
-.openerp .searchview_extended_delete_group span,
-.openerp .searchview_extended_delete_prop span {
- font-size: 0.9em;
- background: url(/web/static/src/img/icons/gtk-close.png) repeat-y;
- padding-left: 18px;
-}
/* List */
.openerp table.oe-listview-content {
clear: right;
@@ -818,91 +411,19 @@ label.error {
border-bottom-right-radius: 4px;
}
-/* Notebook */
-.openerp .oe_form_notebook {
- padding: 0;
- background: none;
- border-width: 0;
-}
-.openerp .oe_form_notebook .ui-tabs-panel {
- padding: 4px;
- -moz-border-radius-topright: 4px;
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
-}
-.openerp .oe_form_notebook ul.ui-tabs-nav {
- padding-left: 0;
- background: transparent;
- border-width: 0;
- border-radius: 0;
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- line-height: 0.8em;
- font-size: 95%;
- color: #555;
-}
-.openerp .oe_form_notebook ul.ui-tabs-nav li {
- font-weight: bold;
-}
-.openerp .oe_form_notebook .ui-tabs-panel {
- background: #f9f9f9;
- border-width: 1px;
-}
-.openerp .oe_form_notebook .ui-tabs-selected {
- background: #f9f9f9;
-}
-/* Unedit Form */
-.openerp .field_char,
-.openerp .field_date,
-.openerp .field_float,
-.openerp .field_selection,
-.openerp a.oe_form_uri {
- vertical-align: middle;
- padding-top: 3px;
- font-size: 90%;
- color: #222;
-}
-.openerp a.oe_form_uri {
- color: #9A0404;
- line-height: 12px;
-}
-
-
-
/* Form */
-.openerp .oe_form_button_save_dirty {
- display: none;
-}
-.openerp .oe_form_dirty > .oe_form_header > .oe_form_buttons > .oe_form_button_save {
- color: white;
- background: #dc5f59;
- background: -moz-linear-gradient(#dc5f59, #b33630);
- background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background: -webkit-linear-gradient(#dc5f59, #b33630);
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -box-shadow: none;
- font-weight: bold;
-}
-.openerp .oe_form_frame_cell input[type="checkbox"] {
+.openerp .oe_form_group_cell input[type="checkbox"] {
margin-top: 3px;
vertical-align: middle;
}
-.openerp .oe_form_frame_cell .input[type="text"] {
+.openerp .oe_form_group_cell .input[type="text"] {
padding-bottom: 1px;
}
-.openerp table.oe_frame td {
+.openerp table.oe_form_group td {
color: #4c4c4c;
}
-.openerp td.oe_form_frame_cell {
- padding: 2px;
- position: relative;
-}
-.openerp .oe_frame.oe_forms {
- clear: both;
-}
-.openerp table.oe_frame {
+.openerp table.oe_form_group {
color: #4c4c4c;
}
.openerp fieldset.oe_group_box {
@@ -916,22 +437,6 @@ label.error {
.openerp fieldset.oe_group_box legend {
font-weight: bold;
}
-.openerp td.oe_form_frame_cell {
- padding: 2px;
- position: relative;
-}
-.openerp td.oe_form_field_translatable,
-.openerp td.oe_form_field_many2one,
-.openerp td.oe_form_field_date,
-.openerp td.oe_form_field_datetime {
- white-space: nowrap;
-}
-.openerp td.oe_form_field_boolean {
- padding-top: 4px;
-}
-.openerp td.oe_form_frame_cell.oe_form_group {
- padding: 0;
-}
.openerp .required.error {
border: 1px solid #900;
}
@@ -949,66 +454,6 @@ label.error {
margin-right: 3px;
}
-
-.openerp label.oe_label_help, .openerp label.oe_label,
-.openerp .oe_form_paragraph,
-.openerp .oe_form_field_statusbar,
-.openerp .oe_forms input[type="text"],
-.openerp .oe_forms input[type="password"],
-.openerp .oe_forms input[type="file"],
-.openerp .oe_forms select,
-.openerp .oe_forms .oe_button,
-.openerp .oe_forms textarea {
- font-size: 85%;
-}
-
-.openerp label.oe_label_help, .openerp label.oe_label {
- display: block;
- color: #4c4c4c;
- font-weight: normal;
-}
-.openerp label.oe_label_help {
- cursor: help;
-}
-.openerp .oe_form_frame_cell .oe_label, .openerp .oe_form_frame_cell .oe_label_help {
- font-weight: normal;
-}
-.openerp #tiptip_content {
- font-size: 12px;
-}
-.openerp .oe_tooltip_string {
- color: #FD5;
- font-weight: bold;
- font-size: 13px;
-}
-.openerp .oe_tooltip_help {
- white-space: pre-wrap;
-}
-.openerp .oe_tooltip_technical {
- padding: 0 0 4px 0;
- margin: 5px 0 0 15px;
- list-style: circle;
-}
-.openerp .oe_tooltip_technical_title {
- font-weight: bold;
-}
-
-.openerp .oe_forms label.oe_label, .openerp .oe_forms label.oe_label_help {
- margin: 3px 0 0 3px;
- white-space: nowrap;
-}
-.openerp .oe_forms .searchview_group_content label.oe_label, .openerp .searchview_group_content .oe_forms label.oe_label_help { /* making a distinction between labels in search view and other labels */
- margin: 3px 0 0 3px;
-}
-
-.openerp label.oe_label_help span {
- font-size: 80%;
- color: darkgreen;
- vertical-align:top;
- position: relative;
- top: -4px;
- padding: 0 2px;
-}
.openerp .oe_align_left {
text-align: left;
}
@@ -1018,12 +463,12 @@ label.error {
.openerp .oe_align_center {
text-align: center;
}
-.openerp .oe_forms .oe_form_paragraph {
+.openerp .oe_form .oe_form_paragraph {
margin: 3px 0 0 0;
white-space: normal;
}
-.openerp .oe_forms .oe_form_paragraph.oe_multilines {
+.openerp .oe_form .oe_form_paragraph.oe_multilines {
white-space: pre;
}
@@ -1033,13 +478,13 @@ label.error {
margin: 0 0.5em 0 0;
}
-.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon,
-.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon {
+.openerp .oe_form .oe-listview th.oe-sortable .ui-icon,
+.openerp .oe_form .oe-listview th.oe-sortable .ui-icon {
height: 100%;
margin-top: -9px;
}
-.openerp table.oe_frame .oe-listview-content td {
+.openerp table.oe_form_group .oe-listview-content td {
color: inherit;
}
@@ -1047,19 +492,19 @@ label.error {
.openerp .oe_form_readonly {
}
-.openerp .oe_form_readonly .oe_form_frame_cell .field_text,
+.openerp .oe_form_readonly .oe_form_group_cell .field_text,
.openerp .oe_form_readonly .field_char,
.openerp .oe_form_readonly .field_int,
.openerp .oe_form_readonly .field_float,
.openerp .oe_form_readonly .field_email,
.openerp .oe_form_readonly .field_date,
.openerp .oe_form_readonly .field_selection,
-.openerp .oe_forms_readonly .oe_form_field_many2one {
+.openerp .oe_form_readonly .oe_form_field_many2one {
padding: 3px 2px 2px 2px;
background-color: white;
height: 17px;
}
-.openerp .oe_form_readonly .oe_form_frame_cell .field_text {
+.openerp .oe_form_readonly .oe_form_group_cell .field_text {
height: auto;
}
.openerp .oe_form_readonly .field_datetime {
@@ -1084,211 +529,34 @@ label.error {
.openerp .oe_form_readonly .oe_form_field_text_html div.field_text_html {
white-space: pre-wrap;
}
-.openerp .oe_form_readonly .oe_form_frame_cell .field_text {
+.openerp .oe_form_readonly .oe_form_group_cell .field_text {
min-height:100px;
}
/* Inputs */
-.openerp .oe_forms input[type="text"],
-.openerp .oe_forms input[type="password"],
-.openerp .oe_forms input[type="file"],
-.openerp .oe_forms select,
-.openerp .oe_forms textarea {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- -ms-box-sizing: border-box;
- box-sizing: border-box;
- padding: 0 2px;
- margin: 0 2px;
- border: 1px solid #999;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- background: white;
- min-width: 90px;
- color: #1f1f1f;
-}
-
-.openerp .oe_forms input.field_many2one,
-.openerp .oe_forms input.field_binary,
-.openerp .oe_forms input.field_binary,
-.openerp .oe_forms input.field_email,
-.openerp .oe_forms input.field_url {
- border-right: none;
- -webkit-border-top-right-radius: 0px;
- -webkit-border-bottom-right-radius: 0px;
- -moz-border-radius-topright: 0px;
- -moz-border-radius-bottomright: 0px;
- border-top-right-radius: 0px;
- border-bottom-right-radius: 0px;
-}
-.openerp .oe_button.oe_field_button {
- -webkit-border-top-left-radius: 0px;
- -webkit-border-bottom-left-radius: 0px;
- -moz-border-radius-topleft: 0px;
- -moz-border-radius-bottomleft: 0px;
- border-top-left-radius: 0px;
- border-bottom-left-radius: 0px;
- margin-right:-1px;
- height: 22px;
-}
-
-.openerp .oe_form_field_email button img,
-.openerp .oe_form_field_url button img {
- vertical-align: top;
-}
/* vertically recentering filter management select tag */
.openerp select.oe_search-view-filters-management {
margin-top:2px;
}
-.openerp .oe_forms select{
- padding-top: 2px;
-}
-.openerp .oe_forms input[readonly],
-.openerp .oe_forms select[readonly],
-.openerp .oe_forms textarea[readonly],
-.openerp .oe_forms input[disabled],
-.openerp .oe_forms select[disabled],
-.openerp .oe_forms textarea[disabled]{
- background: #E5E5E5 !important;
- color: #666;
-}
-.openerp .oe_forms textarea {
- resize:vertical;
-}
-.openerp .oe_forms input[type="text"],
-.openerp .oe_forms input[type="password"],
-.openerp .oe_forms input[type="file"],
-.openerp .oe_forms select,
-.openerp .oe_forms .oe_button {
- height: 22px;
-}
-.openerp .oe_forms input.field_datetime {
- min-width: 11em;
-}
-.openerp .oe_forms .oe_form_button .oe_button {
- color: #4c4c4c;
- white-space: nowrap;
- min-width: 100%;
- width: 100%;
-}
@-moz-document url-prefix() {
/* Strange firefox behaviour on width: 100% + white-space: nowrap */
- .openerp .oe_forms .oe_form_button .oe_button {
+ .openerp .oe_form .oe_form_button .oe_button {
width: auto;
}
}
/* IE Hack - for IE < 9
* Avoids buttons overflow
* */
-.openerp .oe_forms .oe_form_button .oe_button {
+.openerp .oe_form .oe_form_button .oe_button {
min-width: auto\9;
}
-.openerp .oe_forms .button {
- height: 22px;
-}
-.openerp .oe_forms .oe_button span {
- position: relative;
- vertical-align: top;
-}
-.openerp .oe_input_icon {
- cursor: pointer;
- margin: 3px 0 0 -21px;
- vertical-align: top;
-}
-.openerp .oe_datepicker_container {
- display: none;
-}
-.openerp .oe_datepicker_root {
- display: inline-block;
-}
-.openerp .oe_form_frame_cell .oe_datepicker_root {
- width: 100%;
-}
-.openerp .oe_input_icon_disabled {
- position: absolute;
- cursor: default;
- opacity: 0.5;
- filter:alpha(opacity=50);
- right: 5px;
- top: 3px;
-}
-.openerp .oe_trad_field.touched {
- border: 1px solid green !important;
-}
-
-/* http://www.quirksmode.org/dom/inputfile.html
- * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
- */
-.openerp .oe-binary-file-set {
- overflow: hidden;
- position: relative;
-}
-.openerp input.oe-binary-file {
- z-index: 0;
- line-height: 0;
- font-size: 12px;
- position: absolute;
- /* Should be adjusted for all browsers */
- top: 1px;
- right: 10px;
- opacity: 0;
- filter: alpha(opacity = 0);
- -ms-filter: "alpha(opacity=0)";
- margin: 0;
- padding:0;
-}
-/* Widgets */
-.openerp .separator {
- border: 0 solid #666;
-}
-.openerp .separator.horizontal {
- font-weight: bold;
- border-bottom-width: 1px;
- margin: 3px 4px 3px 1px;
- height: 17px;
- font-size: 95%;
-}
-.openerp .separator.horizontal:empty {
- height: 5px;
-}
-.openerp .oe_form_frame_cell.oe_form_separator_vertical {
- border-left: 1px solid #666;
-}
-.openerp td.required input, .openerp td.required select, .openerp td.required textarea {
- background-color: #D2D2FF !important;
-}
-.openerp td.invalid input, .openerp td.invalid select, .openerp td.invalid textarea {
- background-color: #F66 !important;
- border: 1px solid #D00 !important;
-}
-.openerp div.oe-progressbar span {
- position: absolute;
- margin-left: 10px;
- margin-top: 5px;
- font-weight: bold;
-}
/* jQuery UI override */
.openerp .ui-widget {
font-size: 1em;
}
-.openerp .oe_form_field_progressbar .ui-progressbar {
- height: 22px;
- font-size: 10px;
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- -ms-box-sizing: border-box;
- box-sizing: border-box;
- border: 1px solid #999;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- background: white;
- min-width: 90px;
-}
.openerp tbody.ui-widget-content {
margin-bottom: 10px;
border-spacing: 4px;
@@ -1296,10 +564,6 @@ label.error {
.openerp .ui-widget-header {
background: white none;
}
-/* progress bars */
-.openerp .ui-progressbar .ui-widget-header {
- background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
-}
/* Sidebar */
.openerp .view-manager-main-table {
@@ -1318,7 +582,7 @@ label.error {
background: url("/web/static/src/img/sep-a.gif") 0 100% repeat-x;
margin:6px 0 6px 2px;
}
-.openerp .oe_form_frame_cell .oe-view-manager-header { /* Trick: remove the background when element is in a formular */
+.openerp .oe_form_group_cell .oe-view-manager-header { /* Trick: remove the background when element is in a formular */
background: none;
}
@@ -1354,45 +618,6 @@ label.error {
text-decoration: underline;
color: inherit;
}
-.openerp .oe-view-manager-logs {
- clear: both;
- background: #fff;
- margin: 0.25em 0;
- font-size: 85%;
- color: #4C4C4C;
- position: relative;
- overflow: hidden;
-}
-.openerp .oe-view-manager-logs ul {
- margin: 0;
- padding: 0 10px;
- list-style: none;
-}
-.openerp .oe-view-manager-logs li:before {
- content: '\2192 ';
-}
-.openerp .oe-view-manager-logs a {
- text-decoration: none;
- color: inherit;
-}
-/* only display first three log items of a folded logs list */
-.openerp .oe-view-manager-logs.oe-folded li:nth-child(n+4) {
- display: none;
-}
-/* display link to more logs if there are more logs to view and the logview is
- currently folded */
-.openerp .oe-view-manager-logs a.oe-more-logs {
- display: none;
-}
-.openerp .oe-view-manager-logs.oe-folded.oe-has-more a.oe-more-logs {
- display: block;
-}
-.openerp .oe-view-manager-logs a.oe-remove-everything {
- position: absolute;
- top: 0;
- right: 0;
- cursor: pointer;
-}
.openerp .view-manager-main-sidebar {
width: 180px;
@@ -1810,228 +1035,6 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after {
margin: 0 20px;
}
-.openerp .oe_vm_switch {
- margin:2px 0 0 0;
-}
-
-.openerp .oe_vm_switch_form,
-.openerp .oe_vm_switch_page,
-.openerp .oe_vm_switch_tree,
-.openerp .oe_vm_switch_list,
-.openerp .oe_vm_switch_graph,
-.openerp .oe_vm_switch_gantt,
-.openerp .oe_vm_switch_calendar,
-.openerp .oe_vm_switch_kanban,
-.openerp .oe_vm_switch_diagram {
- background: url("/web/static/src/img/views-icons-a.png") repeat-x scroll left top transparent;
- overflow: hidden;
- width: 22px;
- height: 21px;
- border: none;
- background-position: 0px 0px;
-}
-
-.openerp .oe_vm_switch_form span,
-.openerp .oe_vm_switch_page span,
-.openerp .oe_vm_switch_tree span,
-.openerp .oe_vm_switch_list span,
-.openerp .oe_vm_switch_graph span,
-.openerp .oe_vm_switch_gantt span,
-.openerp .oe_vm_switch_calendar span,
-.openerp .oe_vm_switch_kanban span,
-.openerp .oe_vm_switch_diagram span {
- display: none;
-}
-
-.openerp .oe_vm_switch_list {
- background-position: 0px 0px;
-}
-.openerp .oe_vm_switch_list:active,
-.openerp .oe_vm_switch_list:hover,
-.openerp .oe_vm_switch_list:focus,
-.openerp .oe_vm_switch_list[disabled="disabled"] {
- background-position: 0px -21px;
-}
-
-.openerp .oe_vm_switch_tree {
- background-position: 0px 0px;
-}
-.openerp .oe_vm_switch_tree:active,
-.openerp .oe_vm_switch_tree:hover,
-.openerp .oe_vm_switch_tree:focus,
-.openerp .oe_vm_switch_tree[disabled="disabled"] {
- background-position: 0px -21px;
-}
-
-.openerp .oe_vm_switch_form {
- background-position: -22px 0px;
-}
-.openerp .oe_vm_switch_form:active,
-.openerp .oe_vm_switch_form:hover,
-.openerp .oe_vm_switch_form:focus,
-.openerp .oe_vm_switch_form[disabled="disabled"] {
- background-position: -22px -21px;
-}
-
-.openerp .oe_vm_switch_page {
- background-position: -22px 0px;
-}
-.openerp .oe_vm_switch_page:active,
-.openerp .oe_vm_switch_page:hover,
-.openerp .oe_vm_switch_page:focus,
-.openerp .oe_vm_switch_page[disabled="disabled"] {
- background-position: -22px -21px;
-}
-.openerp .oe_vm_switch_graph {
- background-position: -44px 0px;
-}
-.openerp .oe_vm_switch_graph:active,
-.openerp .oe_vm_switch_graph:hover,
-.openerp .oe_vm_switch_graph:focus,
-.openerp .oe_vm_switch_graph[disabled="disabled"] {
- background-position: -44px -21px;
-}
-
-.openerp .oe_vm_switch_gantt {
- background-position: -66px 0px;
-}
-.openerp .oe_vm_switch_gantt:active,
-.openerp .oe_vm_switch_gantt:hover,
-.openerp .oe_vm_switch_gantt:focus,
-.openerp .oe_vm_switch_gantt[disabled="disabled"] {
- background-position: -66px -21px;
-}
-
-.openerp .oe_vm_switch_calendar {
- background-position: -88px 0px;
-}
-.openerp .oe_vm_switch_calendar:active,
-.openerp .oe_vm_switch_calendar:hover,
-.openerp .oe_vm_switch_calendar:focus,
-.openerp .oe_vm_switch_calendar[disabled="disabled"] {
- background-position: -88px -21px;
-}
-.openerp .oe_vm_switch_kanban {
- background-position: -110px 0px;
-}
-.openerp .oe_vm_switch_kanban:active,
-.openerp .oe_vm_switch_kanban:hover,
-.openerp .oe_vm_switch_kanban:focus,
-.openerp .oe_vm_switch_kanban[disabled="disabled"] {
- background-position: -110px -21px;
-}
-
-.openerp .oe_vm_switch_diagram {
- background-position: 0px 0px;
-}
-.openerp .oe_vm_switch_diagram:active,
-.openerp .oe_vm_switch_diagram:hover,
-.openerp .oe_vm_switch_diagram:focus,
-.openerp .oe_vm_switch_diagram[disabled="disabled"] {
- background-position: 0px -21px;
-}
-
-/* Buttons */
-.openerp .oe_button:link,
-.openerp .oe_button:visited,
-.openerp .oe_button {
- display: inline-block;
- border: 1px solid #ababab;
- color: #404040;
- font-size: 12px;
- padding: 3px 10px;
- text-align: center;
- -o-background-size: 100% 100%;
- -moz-background-size: 100% 100%;
- -webkit-background-size: auto auto !important;
- background-size: 100% 100%;
- background: #d8d8d8 none;
- background: none, -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8));
- background: none, -webkit-linear-gradient(#efefef, #d8d8d8);
- background: none, -moz-linear-gradient(#efefef, #d8d8d8);
- background: none, -o-linear-gradient(top, #efefef, #d8d8d8);
- background: none, -khtml-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8));
- background: -ms-linear-gradient(top, #efefef, #d8d8d8);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#efefef', endColorstr='#d8d8d8',GradientType=0 );
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- -o-border-radius: 3px;
- -ms-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -o-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
- -webkit-font-smoothing: antialiased;
- outline: none;
-}
-
-.openerp .oe_button:hover {
- -o-background-size: 100% 100%;
- -moz-background-size: 100% 100%;
- -webkit-background-size: auto auto !important;
- background-size: 100% 100%;
- background: #e3e3e3 none;
- background: none, -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
- background: none, -webkit-linear-gradient(#f6f6f6, #e3e3e3);
- background: none, -moz-linear-gradient(#f6f6f6, #e3e3e3);
- background: none, -o-linear-gradient(top, #f6f6f6, #e3e3e3);
- background: none, -khtml-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
- background: -ms-linear-gradient(top, #f6f6f6, #e3e3e3);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#e3e3e3',GradientType=0 );
- cursor: pointer;
-}
-
-.openerp .oe_button:focus {
- border: 1px solid #80bfff;
- -o-background-size: 100% 100%;
- -moz-background-size: 100% 100%;
- -webkit-background-size: auto auto !important;
- background-size: 100% 100%;
- background: #e3e3e3, none;
- background: none, -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
- background: none, -webkit-linear-gradient(#f6f6f6, #e3e3e3);
- background: none, -moz-linear-gradient(#f6f6f6, #e3e3e3);
- background: none, -o-linear-gradient(top, #f6f6f6, #e3e3e3);
- background: none, -khtml-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
- background: -ms-linear-gradient(top, #f6f6f6, #e3e3e3);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#e3e3e3',GradientType=0 );
- -moz-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -webkit-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -o-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-}
-
-.openerp .oe_button:active,
-.openerp .oe_button.active {
- background: #e3e3e3;
- background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
- background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f;
- background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
- background: -ms-linear-gradient(top, #e3e3e3, #f6f6f6);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e3e3e3', endColorstr='#f6f6f6',GradientType=0 );
- -moz-box-shadow: none, 0 0 0 transparent;
- -webkit-box-shadow: none, 0 0 0 transparent;
- -o-box-shadow: none, 0 0 0 transparent;
- box-shadow: none, 0 0 0 transparent;
-}
-
-.openerp .oe_button.disabled,
-.openerp .oe_button:disabled {
- background: #efefef !important;
- border: 1px solid #d1d1d1 !important;
- font-size: 12px;
- padding: 3px 10px;
- -moz-box-shadow: none !important, 0 0 0 transparent;
- -webkit-box-shadow: none !important, 0 0 0 transparent;
- -o-box-shadow: none !important, 0 0 0 transparent;
- box-shadow: none !important, 0 0 0 transparent;
- color: #aaaaaa !important;
- cursor: default;
- text-shadow: 0 1px 1px white !important;
-}
-
.openerp select.oe_search-view-filters-management {
font-style: oblique;
color: #999999;
@@ -2043,24 +1046,6 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after {
color: black;
}
-/* Debug stuff */
-.openerp .oe_debug_view_log {
- font-size: 95%;
-}
-.openerp .oe_debug_view_log label {
- display: block;
- width: 49%;
- text-align: right;
- float: left;
- font-weight: bold;
- color: #009;
-}
-.openerp .oe_debug_view_log span {
- display: block;
- width: 49%;
- float: right;
- color: #333;
-}
/* Internet Explorer Fix */
a img {
diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.eot b/addons/web/static/src/font/mnmliconsv21-webfont.eot
new file mode 100755
index 00000000000..433ebb82e4b
--- /dev/null
+++ b/addons/web/static/src/font/mnmliconsv21-webfont.eot
Binary files differ
diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.svg b/addons/web/static/src/font/mnmliconsv21-webfont.svg
new file mode 100755
index 00000000000..34b20ff992f
--- /dev/null
+++ b/addons/web/static/src/font/mnmliconsv21-webfont.svg
@@ -0,0 +1,149 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : MIT License
+Designer : Carlos Elas
+Foundry : Carlos Elias
+Foundry URL : httpcarloseliascl
+</metadata>
+<defs>
+<font id="webfontfL60xSgA" horiz-adv-x="1536" >
+<font-face units-per-em="2048" ascent="1638" descent="-410" />
+<missing-glyph horiz-adv-x="512" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="0" />
+<glyph unicode="&#xd;" horiz-adv-x="512" />
+<glyph unicode=" " horiz-adv-x="512" />
+<glyph unicode="&#x09;" horiz-adv-x="512" />
+<glyph unicode="&#xa0;" horiz-adv-x="512" />
+<glyph unicode="!" horiz-adv-x="1556" d="M1302 840q30 -10 52 -31t35 -47t16 -55t-7 -59q-12 -34 -36 -58t-54 -38q-26 -52 -52 -114t-38 -96q-10 -32 -23 -80l-27 -98q32 -38 32 -92q0 -64 -44 -108t-108 -44q-34 0 -65 15t-51 41q-30 2 -62 6t-63 6t-59 4t-46 2q-36 0 -97 -5t-115 -9q-28 -38 -72 -52 q-60 -20 -116 8t-76 86q-14 40 -6 78t32 68l-23 90q-15 60 -31 112q-12 34 -34 86l-44 100q-40 12 -74 50q-40 50 -33 112t55 102q66 52 146 26l33 21q21 13 44 30l47 34q24 17 44 35q34 28 86 76l94 88q4 50 32 84q40 48 102 54t112 -34q28 -22 41 -51t15 -63l91 -89 q49 -47 77 -71q28 -22 77 -57l95 -65q46 18 98 2zM1160 814h-2h2zM1148 804l-4 -4zM1138 792l-4 -4zM1128 780q-4 -4 -4 -6q0 2 4 6zM1118 764q0 -2 -1 -2t-1 -2q0 2 1 2t1 2zM156 552h2h-2zM138 552h2h-2zM214 558h-6h6zM174 552h-2h2zM226 562l4 4zM242 570q0 2 2 4 q-2 -2 -2 -4zM260 584l12 12zM522 64v4v-4zM500 -6v2v-2zM506 10q2 2 4 2q-2 0 -4 -2zM514 26v2v-2zM518 104v-6v6zM520 86q2 -2 2 -6q0 4 -2 6zM920 152q36 52 90 66q22 48 41 97t31 83q18 52 32 112t22 90q-16 20 -25 44t-9 52l-88 84q-48 44 -82 72q-42 34 -93 70t-79 52 q-56 -20 -108 6l-93 -66q-47 -34 -75 -56q-30 -22 -78 -70l-90 -88q0 -50 -32 -92l25 -101q13 -51 23 -81q6 -18 17 -44l23 -54l26 -58l26 -56q48 -20 76 -66q0 2 -2 2q54 -4 114 -7t100 -3q54 0 116 4t92 8zM514 120q-4 12 -8 16q4 -4 8 -16zM1110 744q-2 -6 -2 -16 q0 10 2 16zM1106 722q-2 -4 -2 -6q0 2 2 6zM1132 198v2v-2zM1118 206l-2 2zM1104 212q-2 2 -6 2q4 0 6 -2zM1088 218q-2 2 -6 2q4 0 6 -2zM1072 222h-6h6z" />
+<glyph unicode="&#x22;" horiz-adv-x="1526" d="M16 338q0 70 26 132t72 108t108 72t132 26h18q10 60 40 111t74 89t100 59t120 21q70 0 131 -26t107 -72t73 -107t27 -129q0 -12 -2 -21t-2 -17q12 4 24 4h22q60 0 114 -23t93 -63t62 -93t23 -113t-23 -114t-62 -93t-93 -62t-114 -23h-22h-684q-6 0 -13 -2t-13 -2 q-70 0 -132 27t-108 73t-72 107t-26 131z" />
+<glyph unicode="#" horiz-adv-x="1520" d="M1286 216v-10q0 -44 -16 -81t-44 -65t-65 -44t-79 -16h-802q-42 0 -80 16t-66 44t-44 65t-16 81v12l114 -68l490 -10l494 10zM1352 592v-206l-66 -66v-80l-116 -68l-484 -10h-8l-488 10l-116 68v72l-76 74v206l76 74v90q0 42 16 79t44 66t66 45t80 16h130l102 104h118v86 q4 34 16 58q4 4 6 10t6 10q6 8 22 20q8 4 28 12q10 2 19 3t19 1h56q16 26 46 26q22 0 38 -16t16 -38t-16 -38t-38 -16q-30 0 -46 26h-56q-12 0 -28 -8q-10 -2 -20 -16t-10 -34v-86h114l102 -104h178q42 0 79 -16t65 -45t44 -66t16 -79v-100zM318 284q22 0 37 15t15 37 t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15zM1024 284q22 0 37 15t15 37t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15z" />
+<glyph unicode="$" horiz-adv-x="1460" d="M642 1288q132 0 249 -51t204 -138t138 -204t51 -249q0 -134 -51 -251t-138 -204t-204 -138t-249 -51q-134 0 -252 51t-205 138t-137 204t-50 251q0 132 50 249t137 204t205 138t252 51zM642 1222q-14 0 -25 -1t-25 -1q-4 -2 -11 -2t-13 -2h-10l-46 -8q-24 -4 -46 -12 q58 4 117 0t113 -32q6 -22 27 -26t44 -5t40 -6t15 -31l4 -4q-2 -2 -1 -6t3 2q8 -8 18 -20t16 -22t4 -16t-18 -4q-22 4 -31 -12t-8 -39t11 -44t26 -29q14 -8 25 -5t15 11t-1 18t-19 18q-2 20 11 17t26 -17t20 -32t-9 -28q-4 -2 -6 -4v2q0 -2 -2 -2q-18 -12 -23 -33t-6 -44 l-2 -47q-1 -24 -7 -44q-12 -28 -1 -37t27 -4t30 20t8 37q-8 20 4 27t28 11t28 8t4 18q24 -12 36 -4t14 25t-4 37t-16 32q6 18 13 19t13 -7t8 -21t-4 -23q2 -20 16 -27t26 -11q2 -2 4 -2q12 -4 16 -14t-6 -32q-16 -48 -57 -57t-81 1q-20 -8 -33 -23t-22 -33t-18 -38t-19 -36 q-12 -22 -21 -46t-12 -49t4 -48t27 -41q18 -18 38 -21t40 4t39 20l37 25q30 4 47 -10t22 -36t-1 -47t-20 -43q-2 -6 -2 -14q64 76 101 171t37 203q0 34 -6 64q-12 -6 -27 5t-23 23t-6 18t24 -12q20 0 26 10q-8 52 -30 104q-6 10 -16 16q2 6 1 12t-1 10v2q-36 72 -89 132 t-120 102t-146 66t-163 24zM398 1146q-34 -28 -73 -64l-78 -75q-39 -39 -72 -81t-53 -84q-18 -52 -19 -105t3 -109q16 -52 49 -98t61 -94q18 -24 15 -49t-5 -49v-2q0 -16 3 -30t15 -28q24 -32 54 -59t46 -67v-2q30 -18 61 -32t65 -26q18 2 35 7t35 9v2q28 10 54 24t48 30 q18 24 13 43t-23 33t-43 19t-43 -1q-44 28 -81 67t-85 61q-22 18 -52 25t-58 15t-47 24t-19 54q4 34 -17 55t-43 37q-8 26 8 44t40 23t45 -6t21 -41q16 0 28 13t24 28l25 30q13 15 31 19q10 8 26 10t29 4t20 7t-3 23q10 8 26 14t29 14t18 19t-9 31q-4 20 -18 35t-30 19 t-31 -3t-23 -31q-22 -6 -30 11t-4 39t20 38t40 10q16 0 24 5t8 14t-6 17t-16 14q0 2 -2 2q-4 2 -16 2q-22 12 -17 33t14 43t11 41t-28 29zM570 1056q-2 0 -3 -1t-3 -1q-10 -2 -14 -9t-2 -13t10 -7t20 11q6 4 8 11t-8 7q-4 2 -8 2zM616 1046q-16 -2 -29 -9t-21 -19t-8 -26 t14 -28q10 -12 6 -30l-8 -35q-4 -17 -1 -32t27 -21q8 20 24 39t29 38t20 41t1 46q-4 22 -21 30t-33 6zM496 978q-6 0 -8 -2q6 -12 4 -24t-5 -24t-3 -24t10 -22q12 6 20 22t8 32t-6 29t-20 13zM830 842q-6 0 -6 -5t12 -15q-10 -22 6 -24t26 14q2 12 -13 21t-25 9zM494 752 q-6 -2 -7 -6t15 -14q2 0 9 1t1 -7q22 14 8 21t-26 5zM372 704q-6 0 -12 -2q2 0 2 -2h2q6 -2 9 1t-1 3zM254 542q-20 -4 12 -24q4 -4 11 -9t15 -1q4 12 -3 20t-17 12q-2 2 -10 2h-8z" />
+<glyph unicode="%" horiz-adv-x="1478" d="M180 1142q32 0 53 -22t21 -52v-146h-146v146q0 30 21 52t51 22zM290 886q30 0 52 -22t22 -54v-72q0 -32 -22 -53t-52 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM254 628v-548q0 -32 -21 -53t-53 -21q-30 0 -51 21t-21 53v548h146zM1104 1142q30 0 52 -22 t22 -52v-146h-148v146q0 30 22 52t52 22zM1214 886q30 0 51 -22t21 -54v-72q0 -32 -21 -53t-51 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM1178 628v-548q0 -32 -22 -53t-52 -21t-52 21t-22 53v548h148zM632 6q-30 0 -52 21t-22 53v146h146v-146 q0 -32 -21 -53t-51 -21zM522 262q-32 0 -53 21t-21 53v72q0 30 21 52t53 22h220q30 0 51 -22t21 -52v-72q0 -32 -21 -53t-51 -21h-220zM558 518v550q0 30 22 52t52 22t51 -22t21 -52v-550h-146z" />
+<glyph unicode="&#x26;" d="M1200 276q54 -14 71 -34t11 -44t-30 -48t-54 -46t-60 -38l-46 -24q-26 -12 -83 -25t-125 -25t-135 -20t-111 -8t-112 9t-136 21t-124 25t-76 23q-12 4 -38 20t-56 38t-56 47t-34 49t7 45t69 35q80 20 121 68t59 114t20 142t2 154q0 76 38 128t84 84q52 36 122 58 q-2 44 10 80q10 30 34 56t70 26t70 -26t34 -56q10 -36 10 -80q68 -22 122 -58q46 -32 84 -84t38 -128q0 -78 2 -154t20 -142t59 -114t119 -68zM660 88q92 0 175 11t146 26t99 33t36 32q0 12 -27 23t-74 21t-112 16t-139 10q0 -42 -29 -71t-71 -29t-71 29t-29 71 q-76 -4 -140 -10t-111 -16t-74 -21t-27 -23q0 -14 35 -32t95 -33t142 -26t176 -11z" />
+<glyph unicode="'" horiz-adv-x="1252" d="M1188 2h-1186l206 776h140q-32 42 -50 91t-18 105q0 66 25 123t67 99t100 67t124 25t123 -25t100 -67t68 -99t25 -123q0 -108 -66 -196h138zM596 1086q-46 0 -80 -33t-34 -79q0 -48 34 -82t80 -34q48 0 81 34t33 82q0 46 -33 79t-81 33z" />
+<glyph unicode="(" horiz-adv-x="1198" d="M34 506q-18 22 -27 50t-9 57t9 55t27 44l528 432h58q58 0 92 -22t34 -78v-106q0 -20 6 -36t42 -16h306q36 0 68 -31t32 -69v-354q0 -40 -32 -70t-68 -30h-306q-36 0 -42 -14t-6 -34v-100q0 -60 -34 -95t-92 -35q-10 6 -18 12q-8 4 -18 8t-22 4z" />
+<glyph unicode=")" horiz-adv-x="1198" d="M1158 698q18 -22 27 -50t9 -57t-9 -55t-27 -44l-528 -432h-58q-58 0 -92 22t-34 78v106q0 20 -6 36t-42 16h-306q-36 0 -68 31t-32 69v354q0 40 32 70t68 30h306q36 0 42 14t6 34v100q0 60 34 95t92 35l16 -12q8 -4 19 -8t23 -4z" />
+<glyph unicode="*" horiz-adv-x="1638" d="M1532 1222q16 0 28 -12t12 -30v-1080q0 -16 -12 -28t-28 -12h-1492q-16 0 -28 12t-12 28v1080q0 18 12 30t28 12h1492zM588 750q10 10 8 20q4 14 -8 22l-94 94q-4 2 -5 4t-3 2l-206 206q-20 16 -40 0l-96 -96q-6 -8 -6 -20t6 -18l192 -194l-192 -190q-6 -8 -6 -20t6 -20 l96 -96q20 -16 40 0l206 208q4 0 8 4zM1302 448q0 16 -12 28t-28 12h-582q-16 0 -28 -12t-12 -28v-90q0 -16 12 -29t28 -13h582q16 0 28 13t12 29v90z" />
+<glyph unicode="+" horiz-adv-x="1340" d="M1010 1280q62 0 105 -20t69 -51t38 -72t12 -83v-840q0 -42 -12 -81t-38 -71t-69 -51t-105 -19h-786q-42 0 -81 19t-71 51t-51 71t-19 81v840q0 42 19 83t51 72t71 51t81 20h786zM1010 720h-280v334h-226v-334h-280v-226h280v-280h226v280h280v226z" />
+<glyph unicode="," horiz-adv-x="1328" d="M1072 566l146 -146q28 -28 42 -65t14 -75t-14 -74t-42 -62l-72 -72q-28 -28 -64 -42t-73 -14t-74 14t-65 42l-146 146zM622 1016l358 -360l-348 -346l-358 358zM384 628q16 -16 36 -16t36 16q14 12 14 33t-14 35q-16 16 -36 15t-36 -17q-14 -12 -14 -33t14 -33zM662 490 q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -34q14 -16 35 -16t33 16q16 14 16 34t-16 36zM768 594q-16 14 -36 14t-36 -14q-14 -16 -14 -37t14 -37q16 -12 36 -12t36 14q14 16 14 36t-14 36zM558 594q-14 14 -34 14t-34 -14q-16 -16 -16 -37t16 -37q12 -12 33 -12t35 12 q16 16 16 37t-16 37zM870 694q-14 16 -34 17t-36 -15q-14 -14 -14 -35t14 -33q16 -16 36 -16t34 16q16 12 16 33t-16 33zM662 696q-12 16 -33 16t-35 -16q-16 -14 -16 -35t16 -33q14 -16 35 -16t33 16q14 12 14 33t-14 35zM768 800q-16 16 -36 16t-36 -16q-14 -14 -14 -35 t12 -33q16 -16 37 -16t37 16q14 14 14 34t-14 34zM558 800q-14 16 -34 16t-34 -16q-16 -14 -16 -34t16 -32q14 -16 34 -16t34 16q16 12 16 32t-16 34zM662 834q16 16 16 36t-16 36q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -36q14 -14 35 -14t33 14zM144 1218q26 28 62 42 t74 14t75 -15t67 -43l108 -112l-344 -344l-114 108q-28 30 -42 66t-14 74t14 74t42 64z" />
+<glyph unicode="-" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
+<glyph unicode="." horiz-adv-x="1072" d="M0 1184q0 44 31 75t77 31h642q46 0 77 -31t31 -75v-1074q0 -44 -31 -75t-77 -31h-642q-46 0 -77 31t-31 75v1074zM194 1074q-24 0 -42 -18t-18 -42v-692q0 -26 18 -43t42 -17h470q24 0 42 17t18 43v692q0 24 -18 42t-42 18h-470zM362 144q0 -28 20 -48t48 -20t47 20 t19 48t-19 47t-47 19t-48 -19t-20 -47zM542 1158q0 28 -28 28h-170q-10 0 -19 -8t-9 -20q0 -10 9 -19t19 -9h170q12 0 20 9t8 19z" />
+<glyph unicode="/" horiz-adv-x="1224" d="M904 274q16 -14 16 -34t-16 -36q-14 -14 -34 -14t-34 14l-92 92q-4 -4 -9 -10t-9 -10q-22 -22 -45 -37t-47 -25l-2 -88q-2 -12 -9 -27t-17 -25l-62 -64q-16 -16 -36 -16t-34 16q-16 16 -16 36t16 34l42 42q10 12 12 22l2 44q-44 -4 -89 0t-87 30q-2 0 -4 2q-4 2 -8 6 l-54 54l-4 4l-54 54q-6 2 -6 8l-4 4q-24 44 -27 89t-1 87l-44 -2q-14 0 -22 -12l-42 -42q-16 -14 -35 -14t-35 14q-16 16 -16 35t16 35l62 62q10 10 25 17t27 7l88 4q24 52 64 92l9 9q5 5 11 9l-92 92q-14 14 -14 34t14 36q14 14 34 14t36 -14l48 -50v2l66 -66q14 4 27 7 l29 7l4 120q0 14 9 30t19 26l64 64q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-44 -46q-12 -12 -14 -22l-2 -76q34 10 69 28t69 52q38 40 91 46t99 -22l-64 -64q-12 -12 -13 -28t11 -26q0 -2 2 -4q12 -10 28 -10t26 10l66 68q28 -46 22 -99t-46 -93q-34 -34 -52 -68 t-30 -70l78 4q8 0 20 12l48 46q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-62 -64q-10 -10 -27 -18t-29 -8l-120 -6q-4 -14 -7 -27l-7 -29l64 -64z" />
+<glyph unicode="0" horiz-adv-x="1114" d="M128 1250q30 8 60 3t56 -21t45 -42t23 -56q4 -48 5 -97t1 -97q124 0 246 2t244 -4q46 -8 83 -41t43 -81q8 -30 2 -61t-23 -57t-43 -45t-56 -23q-126 -10 -250 -7t-250 1q2 -46 2 -91t6 -91q8 -44 41 -79t79 -39q94 -8 187 -5t187 -5q40 -6 69 -34t43 -65t9 -77t-33 -70 q-14 -26 -37 -40t-49 -20t-54 -7t-54 -1q-96 2 -193 -1t-189 25q-70 20 -130 64t-103 102t-68 127t-25 143q-4 150 -3 300t1 300q-4 32 3 64t24 58t43 45t58 23z" />
+<glyph unicode="1" horiz-adv-x="1198" d="M874 774q-20 -10 -28 -26t-9 -34t4 -36t11 -36q6 -16 19 -33t21 -33q12 -28 14 -59t-2 -63q-4 -28 -18 -52t-34 -45t-42 -40l-42 -35q-24 -18 -48 -26t-44 -16t-34 -23t-18 -49t-6 -48t-3 -18t-1 -6t-2 -14q0 -2 16 -4t37 -4l41 -4q20 -2 28 -6t21 -12t24 -17t19 -18 t8 -11q0 -4 -45 -5t-100 -1t-102 1t-53 5q-6 2 2 13t19 23t21 24t10 16t-1 23l-2 40q-1 21 -5 40t-10 23q-14 10 -42 16t-58 16q-44 14 -98 11t-100 -3q-70 0 -141 -12t-141 -12l-40 60q62 8 114 16l78 11q36 5 36 9t-24 14t-52 20q-34 12 -74 28l64 16q24 6 42 10t14 2 q58 22 123 30t123 32q16 6 52 32t76 60t75 68t47 56l22 43l19 37q9 18 17 37t14 45l15 65q7 31 39 49q28 18 64 18t68 -8q24 -6 53 -31t56 -56t46 -60t23 -43q2 -4 -23 -5t-55 -1t-58 1t-32 -1q-4 0 -8 -4z" />
+<glyph unicode="2" horiz-adv-x="1460" d="M1282 1282v-1280h-1282v1280h1282zM1106 1190h-174q-70 -2 -115 -22t-71 -52t-36 -76t-10 -94v-110h-116v-196h116v-548h232v548h152l22 196h-174v110q0 26 12 42t42 16h120v186z" />
+<glyph unicode="3" d="M1024 976q78 0 146 -29t119 -80t81 -118t30 -143q0 -78 -30 -147t-81 -120t-119 -81t-146 -30q-102 0 -188 52t-132 138q-52 -86 -137 -138t-189 -52q-78 0 -146 30t-119 81t-80 120t-29 147q0 76 29 143t80 118t119 80t146 29q104 0 189 -51t137 -135q46 84 132 135 t188 51zM1218 410q82 76 82 196q0 110 -82 192q-36 36 -86 57t-108 21q-56 0 -105 -21t-85 -57q-40 -40 -61 -89t-21 -103q0 -58 21 -109t61 -87q74 -80 190 -80q58 0 108 20t86 60z" />
+<glyph unicode="4" horiz-adv-x="1466" d="M0 1284h1286v-1286h-1286v1286zM1170 114v528h-526v526h-526v-528h526v-526h526z" />
+<glyph unicode="5" horiz-adv-x="1240" d="M314 1072q0 44 28 66q24 26 72 26q42 0 66 -26q26 -24 26 -68v-186q0 -22 -5 -40t-21 -30q-26 -26 -70 -26q-42 0 -68 26q-28 22 -28 72v186zM382 880q0 -16 8 -24q4 -4 20 -4t20 4q10 10 10 24v194q0 14 -10 20q-8 8 -20 8q-6 0 -10 -2t-10 -6q-8 -6 -8 -20v-194z M156 988l-90 294h76l48 -196h6l46 196h76l-86 -282v-202h-76v190zM610 794q-26 0 -34 14q-6 10 -10 20t-4 28v298h68v-276q0 -8 4 -16q2 -6 16 -6q6 0 18 6q8 12 18 18v274h68v-356h-68v36l-18 -18q-8 -8 -18 -14q-22 -8 -40 -8zM1082 524q4 -50 6 -101t2 -101 q0 -54 -2 -104t-6 -102q0 -66 -47 -111t-111 -45q-94 -4 -188 -7t-192 -3q-100 0 -194 3t-190 7q-64 0 -111 45t-47 111q-4 52 -6 102t-2 104q0 50 2 101t6 101q0 32 13 61t34 51t50 35t61 13q188 6 384 6q194 0 380 -6q32 0 61 -13t50 -35t34 -51t13 -61zM232 500h76v68 h-226v-68h76v-416h74v416zM500 442h-70v-270q-12 -16 -20 -22q-10 -6 -16 -6q-12 0 -16 4q-4 8 -4 20v274h-66v-298q0 -32 12 -50q12 -14 34 -14q20 0 40 10q10 4 19 14t17 20v-40h70v358zM754 356q0 44 -18 68q-16 22 -46 22q-16 0 -30 -8q-18 -6 -30 -26v156h-68v-484h68 v30q6 -10 14 -16t16 -10q14 -8 32 -8q30 0 46 20q8 12 12 25t4 33v198zM998 346q0 54 -22 78q-28 28 -72 28t-72 -28q-12 -16 -20 -35t-8 -43v-160q0 -50 26 -82q22 -28 68 -28q52 0 76 24q24 30 24 86v28h-68v-26q0 -16 -1 -27t-5 -15q-8 -8 -24 -8q-10 0 -22 8 q-6 12 -6 42v66h126v92zM900 388q18 0 24 -6q6 -16 6 -36v-36h-58v36q0 20 6 36q8 6 22 6zM660 390q12 0 20 -8q4 -8 4 -28v-184q0 -24 -4 -26q-8 -8 -20 -8q-4 0 -16 6q-8 0 -14 10v224q4 4 14 12q4 0 8 1t8 1z" />
+<glyph unicode="6" horiz-adv-x="1442" d="M1214 802q-12 -178 -248 -482q-250 -320 -420 -320q-102 0 -178 196l-48 178l-50 178q-54 196 -116 196q-16 0 -96 -56l-58 74q46 38 89 78l89 80q58 52 104 78t74 30q140 12 176 -194q18 -112 30 -180t16 -96q42 -184 92 -184q36 0 114 118q74 122 82 182 q10 104 -82 104q-22 0 -45 -5t-47 -15q92 290 344 284q186 -8 178 -244z" />
+<glyph unicode="7" horiz-adv-x="1466" d="M1174 1284q46 0 78 -32t32 -78v-1066q0 -46 -32 -78t-78 -32h-1066q-46 0 -78 32t-32 78v1066q0 46 32 78t78 32h1066zM952 352q-74 -50 -152 -50q-46 0 -76 22q-24 16 -36 38q-8 24 -8 112v260h240v160h-240v256h-144q-12 -78 -36 -130q-14 -26 -31 -48t-39 -38 q-42 -38 -100 -56v-144h110v-354q0 -68 16 -106t54 -68q18 -16 40 -29t50 -23q54 -16 122 -16q60 0 112 12q28 4 57 16t61 28v158z" />
+<glyph unicode="8" horiz-adv-x="1454" d="M682 702q0 2 2 2v-2h-2zM1180 1282q38 0 66 -26t28 -66v-1100q0 -38 -28 -65t-66 -27h-1088q-40 0 -67 27t-27 65v1100q0 40 27 66t67 26h1088zM382 788h-190v-580h190v580zM288 866q50 0 79 29t29 73q0 40 -29 69t-79 29q-48 0 -78 -29t-30 -69q0 -44 29 -73t77 -29h2z M1078 540q0 134 -63 198t-159 64q-38 0 -66 -10t-48 -24t-34 -32l-24 -32v84h-194q0 -10 1 -41t1 -74v-94v-103q0 -122 -2 -268h194v324v24q0 12 4 22q10 26 35 49t65 23q54 0 76 -37t22 -95v-310h192v332z" />
+<glyph unicode="9" horiz-adv-x="1310" d="M1162 514q24 -40 38 -85t14 -95q0 -70 -27 -132t-73 -108t-108 -73t-134 -27q-50 0 -94 14t-84 38q-40 -8 -88 -8q-118 0 -220 44t-179 121t-121 179t-44 220q0 48 8 86q-24 40 -38 85t-14 95q0 72 27 134t73 108t108 73t132 27q50 0 95 -14t85 -38q38 8 88 8 q116 0 219 -44t180 -121t121 -180t44 -219q0 -50 -8 -88zM874 344q16 28 26 56t10 62q0 52 -22 90q-16 36 -56 62q-40 24 -90 42q-26 8 -55 15l-61 15l-44 10q-18 4 -30 8q-20 6 -44 16q-22 12 -32 28q-12 12 -12 34q0 34 36 56q30 22 94 22t92 -20q14 -10 26 -26t22 -34 q14 -30 30 -42q18 -14 42 -14q32 0 50 22q20 20 20 48q0 14 -4 28t-10 30q-16 26 -52 56q-32 26 -86 44q-52 16 -120 16q-90 0 -152 -24q-68 -28 -102 -70q-32 -44 -32 -106q0 -64 30 -102q38 -44 90 -66q54 -24 138 -42q28 -8 53 -13t43 -11q40 -14 58 -34q24 -22 24 -54 q0 -42 -44 -70q-42 -32 -108 -32q-48 0 -80 16q-28 16 -46 36q-8 10 -15 25l-15 31q-14 30 -30 44t-42 14q-32 0 -52 -18q-20 -24 -20 -46q0 -24 8 -47t24 -47q32 -46 86 -76q76 -40 186 -40q92 0 164 30q34 14 59 34t45 44z" />
+<glyph unicode=":" horiz-adv-x="1382" d="M1002 390l128 -16l-14 -112l-128 16zM1028 462l-2 504l218 -26l-122 -490zM466 956q108 0 194 -28t148 -77t95 -114t33 -139t-34 -139t-96 -113t-149 -76t-191 -28t-190 28t-148 76t-96 113t-34 139t33 139t94 114t148 77t193 28zM754 710q-8 0 -23 1t-32 1h-33h-28h-33 h-37h-32h-20l4 -28q6 0 34 -3t40 -7q-2 -8 -18 -23l-35 -32q-19 -17 -37 -31t-22 -20q-16 22 -54 70t-68 94q6 2 18 3t26 1t26 1t16 3l4 20q-4 0 -22 1t-40 1h-45h-37q-46 -4 -86 -1t-46 -1v-24q6 0 18 -2t25 -3t23 -3t14 -4q12 -10 38 -38t52 -60t46 -59t22 -33 q2 -10 2 -21v-17v-16v-37q0 -9 -4 -13q-10 -10 -39 -10h-39l-6 -26h26q18 0 37 1t36 1h27h144v26h-17h-27q-14 0 -27 1t-15 1q-2 10 -4 26t-2 28v20q0 20 4 36q2 10 26 32t52 45t53 41t31 20l34 7q24 5 32 5z" />
+<glyph unicode=";" horiz-adv-x="1204" d="M594 628q22 -12 36 -35t14 -51q0 -26 -14 -48t-36 -36v-64h-98v64q-22 14 -36 36t-14 48q0 28 14 51t36 35v212h98v-212zM978 870q52 -68 82 -151t30 -177q0 -112 -43 -211t-117 -173t-174 -117t-212 -43t-211 43t-173 117t-117 173t-43 211q0 108 38 202t105 167 t158 119t195 56v102h-50v98h198v-98h-50v-102q78 -8 149 -36t129 -72l110 108l106 -106zM544 98q92 0 173 35t142 95t96 141t35 173t-35 173t-96 142t-142 96t-173 35t-173 -35t-141 -96t-95 -142t-35 -173t35 -173t95 -141t141 -95t173 -35z" />
+<glyph unicode="&#x3c;" horiz-adv-x="1198" d="M614 1290q78 0 145 -29t118 -80t80 -119t29 -146q0 -50 -18 -138t-47 -190t-68 -207t-81 -190t-85 -139t-79 -54q-44 0 -89 54t-85 139t-75 190t-61 207t-41 189t-15 139q0 78 29 146t80 119t118 80t145 29zM614 828q58 0 100 42t42 100t-42 100t-100 42t-100 -42 t-42 -100t42 -100t100 -42z" />
+<glyph unicode="=" horiz-adv-x="1400" d="M1270 930q34 0 59 -25t25 -61v-650q0 -34 -25 -58t-59 -24h-1186q-34 0 -58 24t-24 58v650q0 36 24 61t58 25h252l54 102q0 12 9 23t25 11h510q12 0 22 -11t10 -23l52 -102h252zM676 192q70 0 132 27t108 73t73 107t27 131t-27 132t-73 108t-108 73t-132 27t-131 -27 t-107 -73t-73 -108t-27 -132t27 -131t73 -107t107 -73t131 -27zM886 530q0 -42 -17 -80t-45 -67t-66 -45t-82 -16q-42 0 -80 16t-67 45t-45 67t-16 80q0 44 16 82t45 66t67 45t80 17q44 0 82 -17t66 -45t45 -66t17 -82z" />
+<glyph unicode="&#x3e;" horiz-adv-x="1204" d="M146 16q-24 -22 -57 -22t-59 22q-22 26 -22 59t22 57l1028 1024q24 22 58 22t58 -22q22 -24 22 -58t-22 -58zM530 12q-22 -22 -56 -22t-58 22q-24 24 -24 58t24 58l650 652q26 24 59 24t57 -24q22 -22 22 -56t-22 -58zM940 30q-24 -22 -58 -22t-56 22q-22 26 -22 59 t22 57l228 228q24 24 58 24t58 -24q22 -22 22 -56t-22 -56z" />
+<glyph unicode="?" horiz-adv-x="1400" d="M646 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM650 258q6 42 -14 76q-24 32 -64 42q-44 10 -76 -14q-38 -22 -44 -62q-10 -44 14 -76 q24 -36 64 -44q44 -10 76 14q36 24 44 64zM978 776q18 80 -12 152q-26 70 -90 118q-56 46 -144 64q-42 10 -94 6q-50 -2 -102 -22q-26 -10 -46 -23t-38 -29q-36 -36 -44 -82q-8 -40 6 -70q18 -30 52 -38q12 -4 25 -1t23 7q20 12 54 36q16 12 31 22t31 16q26 10 64 4 q44 -8 68 -44q28 -34 18 -74q-12 -52 -42 -66q-26 -16 -82 -22q-30 -2 -52 -3t-34 -5q-24 -6 -30 -36l-26 -128q-8 -38 12 -68q18 -28 60 -38q38 -8 72 10q30 20 38 58l10 46q106 2 180 56q72 56 92 154z" />
+<glyph unicode="@" d="M1368 948q28 0 47 -18t19 -46v-818q0 -28 -19 -47t-47 -19h-1304q-28 0 -47 19t-19 47v818q0 28 19 46t47 18h1304zM1366 806q16 14 20 34t-10 36q-14 20 -36 22t-38 -8l-584 -446l-588 438q-20 12 -40 8t-34 -20q-14 -18 -10 -39t20 -33l620 -458q12 -12 32 -12 q18 0 30 12z" />
+<glyph unicode="A" horiz-adv-x="882" d="M178 1260q12 10 27 10q9 0 19 -4q22 -10 28 -32l88 -264q10 -16 0 -36q-8 -20 -24 -26l-44 -20q-22 -10 -30 -30t2 -42l32 -92l28 -79q14 -41 28 -69q12 -28 31 -63l35 -67l42 -76q8 -22 29 -30t43 2l44 20q16 10 38 0q20 -10 26 -24l138 -242q7 -12 7 -24q0 -10 -5 -20 q-12 -22 -32 -30h-6q-48 -12 -112 -20q-17 -2 -33 -2q-45 0 -85 16q-36 14 -75 49t-77 86t-74 114t-66 131l-36 76q-4 4 -4 5t-2 5l-9 21q-9 21 -19 47t-20 47t-10 23q-30 68 -52 137t-34 131q-13 68 -13 122q0 48 13 82q10 26 29 51t41 46t46 38l44 31z" />
+<glyph unicode="B" horiz-adv-x="1466" d="M1234 742q18 0 34 -16t16 -34v-100q0 -22 -16 -36t-34 -14h-60q-20 0 -39 -13t-27 -33l-34 -84q-8 -20 -5 -42t19 -38l40 -40q16 -16 16 -35t-16 -35l-70 -70q-16 -16 -35 -16t-35 16l-40 40q-16 16 -38 19t-42 -5l-80 -34q-22 -8 -36 -27t-14 -39v-60q0 -18 -14 -34 t-36 -16h-100q-18 0 -31 16t-13 34v60q0 20 -16 39t-34 27l-80 34q-20 8 -42 5t-38 -19l-40 -40q-16 -16 -38 -16t-32 16l-70 70q-16 16 -16 35t16 35l40 40q16 16 18 38t-8 42l-30 84q-8 20 -27 33t-39 13h-60q-18 0 -34 14t-16 36v100q0 18 16 34t34 16l60 -6q20 0 39 16 t27 34l30 80q10 20 8 42t-18 38l-40 46q-16 10 -16 32t16 38l70 64q10 16 32 16t38 -16l40 -40q16 -16 38 -18t42 8l80 36q18 4 34 22t16 38v60q0 22 13 36t31 14h100q50 0 50 -50v-60q0 -20 14 -38t36 -22l80 -36q20 -10 42 -8t38 18l40 40q16 16 35 16t35 -16l70 -64 q16 -16 16 -38t-16 -32l-40 -46q-16 -16 -19 -38t5 -42l34 -80q8 -18 27 -31t39 -13h60zM888 642q0 50 -19 95t-53 78t-80 52t-98 19q-50 0 -95 -19t-78 -52t-52 -78t-19 -95q0 -52 19 -98t52 -80t78 -53t95 -19q52 0 98 19t80 53t53 80t19 98z" />
+<glyph unicode="C" horiz-adv-x="1574" d="M1224 1280q38 0 71 -14t58 -39t39 -58t14 -71v-612q0 -74 -53 -127t-129 -53h-672v-318l-356 318h-14q-38 0 -72 14t-59 39t-39 58t-14 69v612q0 38 14 71t39 58t59 39t72 14h1042z" />
+<glyph unicode="D" horiz-adv-x="1466" d="M820 470v414q0 34 -24 58t-58 24h-12q-34 0 -59 -24t-25 -58v-414q0 -34 25 -58t59 -24h12q34 0 58 24t24 58zM820 214q0 38 -27 63t-63 25q-38 0 -64 -25t-26 -63q0 -40 26 -66t64 -26q36 0 63 26t27 66zM1358 -2h-1258q-58 0 -79 38q-9 16 -9 34q0 26 18 56l628 1086 q28 54 71 54t75 -54l626 -1086q19 -31 19 -58q0 -17 -8 -32q-21 -38 -83 -38z" />
+<glyph unicode="E" horiz-adv-x="1502" d="M1152 842l-322 326l102 106q22 18 53 18t53 -18l220 -220q22 -22 22 -53t-22 -53zM-4 48l58 310q0 30 22 56l678 676l324 -326l-676 -676q-6 -6 -22 -14t-22 -12l-322 -58q-5 -1 -9 -1q-14 0 -23 10t-9 25q0 4 1 10z" />
+<glyph unicode="F" horiz-adv-x="1514" d="M1114 1416l4 -96l-88 34l-88 -34l6 96l-62 74l94 24l50 78l50 -78l92 -24zM770 1512l8 -142l-130 50l-132 -50l10 142l-92 108l138 36l76 118l76 -118l134 -36zM1240 1062l-178 178q-18 18 -42 18t-44 -18l-262 -260h4q17 0 56 -26q44 -30 98 -82q52 -52 82 -97 q24 -36 24 -52q0 -4 -2 -7l264 260q18 20 18 44t-18 42zM812 814q-56 54 -101 85q-34 23 -48 23q-5 0 -7 -2l-4 -2l-620 -620v-4l-12 -10q-16 -18 -16 -42t16 -42l180 -178q16 -18 41 -18t41 18l60 56h-4l580 578q2 2 2 7q0 14 -22 50q-30 47 -86 101z" />
+<glyph unicode="G" d="M642 1288q134 0 252 -51t205 -140t138 -207t51 -252q0 -132 -51 -250t-138 -206t-205 -139t-252 -51t-252 51t-207 139t-140 206t-51 250q0 134 51 252t140 207t207 140t252 51zM642 202q90 0 169 35t138 94t94 138t35 169t-35 170t-94 140t-138 94t-169 34t-170 -34 t-140 -94t-94 -140t-34 -170t34 -169t94 -138t140 -94t170 -35zM878 638q0 -48 -19 -91t-51 -75t-75 -51t-91 -19q-50 0 -93 19t-76 51t-52 75t-19 91q0 50 19 93t52 76t76 52t93 19q48 0 91 -19t75 -52t51 -76t19 -93z" />
+<glyph unicode="H" horiz-adv-x="1300" d="M1198 1282q36 0 63 -27t27 -63l-46 -466q-4 -26 -18 -40q-22 -22 -52 -22t-50 22l-52 50q-26 20 -52 0l-216 -220q-22 -22 -51 -22t-51 22l-178 178q-22 22 -22 52t22 52l218 216q10 10 10 25t-10 25l-50 52q-22 22 -22 50t22 50q14 18 40 22zM892 416q10 6 14 8l102 104 v-476q0 -22 -16 -38t-40 -16h-900q-22 0 -38 16t-16 38v900q0 24 16 40t38 16h488l-108 -108l-8 -8h-272q-16 0 -27 -11t-11 -27v-702q0 -18 11 -28t27 -10h704q16 0 26 10t10 28v264z" />
+<glyph unicode="I" horiz-adv-x="1204" d="M100 96q-50 50 -75 112t-25 127t25 127t75 112q70 70 161 93q41 10 82 10q50 0 99 -15l418 418l202 -22l22 -200l-158 -40l-22 -140l-150 -32l-18 -136l-72 -72q16 -50 16 -101q0 -40 -10 -80q-22 -91 -92 -161q-50 -50 -112 -75t-127 -25t-127 25t-112 75zM332 330 q-34 34 -83 34t-83 -34t-34 -82t34 -82t83 -34t83 34t34 82t-34 82z" />
+<glyph unicode="J" horiz-adv-x="1460" d="M638 1288q134 0 251 -51t205 -139t139 -205t51 -251t-51 -251t-139 -205t-205 -139t-251 -51t-251 51t-205 139t-139 205t-51 251t51 251t139 205t205 139t251 51zM638 1124q-30 0 -51 -20t-21 -52v-306q-54 -34 -54 -104q0 -54 36 -93t90 -39t92 39t38 93q0 34 -15 61 t-39 43v306q0 32 -23 52t-53 20zM810 574q-28 -76 -104 -104l104 -104q22 -24 52 -24t52 24q22 22 22 53t-22 51z" />
+<glyph unicode="K" horiz-adv-x="1370" d="M442 156l-282 282v402l282 284h402l284 -284v-402l-284 -282h-402zM878 74l332 332v468l-332 332h-468l-332 -332v-468l332 -332h468zM348 26l-318 318q-12 14 -20 33t-8 35v454q0 16 8 36t20 32l318 320q14 10 34 18t34 8h454q16 0 36 -8t32 -18l316 -320q12 -12 22 -32 t10 -36v-454q0 -16 -10 -35t-22 -33l-316 -318q-12 -12 -32 -20t-36 -8h-454q-14 0 -34 8t-34 20z" />
+<glyph unicode="L" horiz-adv-x="1442" d="M642 968q52 80 121 120t141 46q14 1 27 1q58 0 114 -21q69 -26 123 -77t87 -123t33 -160q0 -90 -36 -169t-91 -146t-123 -123l-130 -104l-106 -81l-71 -56l-50 -41q-21 -18 -39 -38q-20 20 -42 38l-52 41l-72 55l-104 82l-129 105q-67 57 -123 123t-92 143t-36 167 q0 88 33 161t87 125t124 78q57 21 115 21q13 0 27 -1q72 -6 141 -46t123 -120z" />
+<glyph unicode="M" horiz-adv-x="1484" d="M1060 444l26 -444l-416 162l-418 -162l26 444l-282 348l432 114l242 378l244 -378l432 -114z" />
+<glyph unicode="N" d="M744 726q48 0 83 -36t35 -88q0 -50 -35 -86t-83 -36q-52 0 -89 36t-37 86q0 52 37 88t89 36zM744 1094q76 0 148 -21t138 -56t124 -79t108 -90q116 -110 214 -246q-98 -138 -214 -246q-50 -46 -108 -90t-124 -78t-138 -55t-148 -21t-148 21t-138 55t-125 78t-109 90 q-118 108 -216 246q98 136 216 246q50 46 109 90t125 79t138 56t148 21zM744 298q62 0 118 24t98 65t66 96t24 119t-24 120t-66 97t-98 65t-118 24q-66 0 -122 -24t-97 -65t-65 -97t-24 -120t24 -119t65 -96t97 -65t122 -24z" />
+<glyph unicode="O" horiz-adv-x="858" d="M276 -2v152q-80 2 -151 22t-117 46l48 184q50 -28 117 -49t145 -21q70 0 111 26t41 72t-39 77t-129 61q-128 44 -207 109t-79 179q0 108 70 184t198 98v152h156v-140q78 -4 134 -18t96 -34l-48 -180q-30 16 -86 36t-142 20q-76 0 -105 -28t-29 -62q0 -38 44 -68t146 -68 q142 -48 203 -118t61 -178q0 -52 -18 -100t-54 -86t-88 -65t-120 -39v-164h-158z" />
+<glyph unicode="P" horiz-adv-x="1460" d="M832 670q-16 -10 -36 -10q-26 0 -43 17t-17 39v104q-60 -2 -113 -25t-93 -63t-63 -93t-23 -113q0 -62 23 -116t63 -94t94 -63t116 -23q54 0 102 18t87 50t65 76t36 96q4 20 25 38t53 18h140q22 0 37 -17t15 -39v-14q-14 -104 -63 -194t-124 -157t-170 -105t-203 -38 q-118 0 -220 44t-179 121t-121 179t-44 220q0 116 44 218t120 178t178 120t218 44v104q0 26 17 41t43 15q10 0 19 -1t17 -7l306 -230q27 -28 27 -55.5t-27 -54.5z" />
+<glyph unicode="Q" horiz-adv-x="1258" d="M204 402q16 -20 12 -46t-28 -40q-20 -16 -46 -11t-42 23q-46 70 -72 148t-26 162v27q0 13 4 27v10q0 6 3 14t3 18v4q4 8 4 13t4 9q0 8 2 17t2 15q4 4 5 8t5 10q0 16 8 32q6 6 6 8v2q12 18 18 40l2 2v2q20 34 43 65t51 59q8 8 16 15t20 15q4 4 6 10q8 4 15 9l15 11h2 q136 94 306 94v108l252 -170l-252 -176v112q-76 0 -140 -26t-116 -66q-2 0 -3 -2t-5 -2l-13 -13q-5 -5 -13 -11v-4q-22 -22 -38 -42t-32 -48h-2q-6 -8 -8 -17t-6 -15v-4q-8 -20 -14 -32v-10q-4 -6 -7 -13t-3 -15t-4 -12q0 -20 -4 -26v-16v-32q-1 -11 -1 -22q0 -52 16 -103 q19 -61 55 -115zM542 -4l-258 170l258 174v-110q70 0 137 25t121 67v4q40 30 69 68t45 86q4 8 7 17l5 15q0 2 4 10q0 4 3 11t3 13t2 8t2 6q0 8 2 14t2 14v8v38q1 12 1 23q0 115 -73 217q-14 22 -9 47t29 37q16 16 36 16q5 0 10 -1q26 -5 42 -25q96 -142 96 -310v-10v-19 q0 -11 -4 -21v-18q0 -8 -2 -13t-2 -13v-10q-6 -6 -6 -20q-4 -6 -4 -14t-4 -16q-4 -4 -4 -12q-4 -10 -7 -21t-7 -21l-4 -6q-50 -118 -150 -198l-6 -4q-6 -8 -13 -12t-19 -12l-2 -4q-64 -46 -141 -69t-159 -23v-106z" />
+<glyph unicode="R" horiz-adv-x="1328" d="M1248 914q24 -24 24 -57t-24 -57l-322 -268q-18 -12 -40 -12q-28 0 -46 18t-18 46v64q0 14 -10 23t-22 9h-194q-28 0 -47 18t-19 46v226q0 28 19 46t47 18h194q12 0 22 10t10 24v64q0 26 18 45t46 19q24 0 40 -14zM350 666q12 14 36 14q28 0 46 -19t18 -45v-66 q0 -12 10 -22t24 -10h194q26 0 45 -18t19 -46v-226q0 -28 -19 -46t-45 -18h-194q-14 0 -24 -9t-10 -23v-64q0 -28 -18 -46t-46 -18q-24 0 -36 12l-326 268q-24 24 -24 57t24 57z" />
+<glyph unicode="S" d="M1184 1288q42 0 72 -30t30 -74v-1082q0 -42 -30 -72t-72 -30h-1084q-42 0 -72 30t-30 72v1082q0 44 30 74t72 30h1084zM244 836q0 -28 17 -45t43 -17h674q28 0 45 17t17 45v388v2h-796v-2v-388zM1140 594q0 20 -15 34t-37 14h-894q-22 0 -36 -14t-14 -34v-446 q0 -20 14 -34t36 -14h894q22 0 37 14t15 34v446zM496 868q0 -10 -8 -19t-20 -9h-106q-10 0 -19 9t-9 19v280q0 12 9 20t19 8h106q28 0 28 -28v-280zM1082 530q0 -24 -26 -24h-830q-26 0 -26 24q0 26 26 26h830q26 0 26 -26zM1082 376q0 -26 -26 -26h-830q-26 0 -26 26t26 26 h830q26 0 26 -26zM1082 212q0 -10 -8 -17t-18 -7h-830q-10 0 -18 7t-8 17t8 19t18 9h830q10 0 18 -9t8 -19z" />
+<glyph unicode="T" horiz-adv-x="1358" d="M1026 840q40 38 40 93t-40 95q-38 38 -92 38t-92 -38q-40 -40 -40 -95t40 -93q38 -40 92 -40t92 40zM36 588l640 636q16 18 37 24t43 4q6 2 20 2l400 -42h6q34 0 56 -20q19 -18 19 -45q0 -7 -1 -15l14 -368q-2 -8 -7 -21t-11 -26t-13 -25t-13 -18l-640 -638 q-28 -28 -65 -28t-65 28l-418 418q-28 28 -28 67t26 67z" />
+<glyph unicode="U" horiz-adv-x="1400" d="M874 492q-12 -24 -5 -53t19 -59l24 -60q12 -30 11 -54t-20 -40t-67 -18q-9 -1 -18 -1q-51 0 -105 20q-63 23 -122 57t-107 72t-74 62h-2q-4 2 -8 6t-9 8t-9 10l-4 2q-22 22 -56 61t-65 88t-55 106t-24 115v11q0 39 13 57q15 20 39 22t54 -8t61 -19t61 -13t52 10 q32 20 70 57t69 77t51 79t20 65q0 24 8 47t27 37t50 15h3q30 0 72 -19q76 -32 129 -67t83 -57q2 -2 5 -4t5 -4q2 -4 5 -6t5 -4q26 -28 64 -78t78 -120q24 -49 24 -84q0 -26 -11 -44q-13 -20 -35 -30t-48 -10q-24 -2 -61 -25t-75 -58t-71 -75t-51 -74zM-6 0l252 466 q14 -18 28 -38l27 -36q15 -20 31 -36t37 -33t41 -31q22 -16 48 -30z" />
+<glyph unicode="V" horiz-adv-x="1418" d="M638 0q-132 0 -249 51t-204 138t-138 204t-51 249t51 249t138 204t204 138t249 51t249 -51t204 -138t138 -204t51 -249t-51 -249t-138 -204t-204 -138t-249 -51zM196 642q0 -92 35 -172t95 -140t140 -95t172 -35q130 0 238 68l-612 612q-68 -108 -68 -238zM1080 642 q0 92 -34 172t-94 140t-141 95t-173 35q-130 0 -238 -68l612 -612q68 108 68 238z" />
+<glyph unicode="W" horiz-adv-x="1132" d="M846 800q24 0 40 -18t16 -42v-684q0 -24 -16 -41t-40 -17h-788q-24 0 -40 17t-16 41v684q0 24 16 42t40 18h42v124q0 72 28 136t76 112t112 76t136 28q74 0 139 -28t113 -76t76 -112t28 -136v-124h38zM678 800v124q0 46 -18 87t-49 72t-72 49t-87 18t-87 -18t-72 -49 t-49 -72t-18 -87v-124h452z" />
+<glyph unicode="X" horiz-adv-x="1484" d="M-8 58v688q0 26 17 42t43 16h624v124q0 48 -18 89t-49 72t-72 49t-89 18t-90 -18t-74 -50l-114 -12l54 -102q-4 -10 -4 -22v-24v-34h-130v34q0 74 28 139t77 114t114 77t139 28t139 -28t114 -77t77 -114t28 -139v-124h38q22 0 39 -16t17 -42v-688q0 -26 -17 -43t-39 -17 h-792q-26 0 -43 17t-17 43z" />
+<glyph unicode="Y" d="M1138 1282l310 -770v-514h-1446v564l294 720h842zM984 506l294 6l-224 594h-652l-218 -572h282v-284h518v256z" />
+<glyph unicode="Z" d="M1452 516v-516h-1452v566l298 722h844zM1284 516l-228 596h-654l-216 -574h282v-286h518v258zM772 488q-18 -16 -45 -16t-43 16l-184 224v24q0 54 42 54h46q8 0 15 3t7 19v130q0 16 13 29t29 13h152q16 0 29 -13t13 -29v-130q0 -16 6 -19t14 -3h42q56 0 56 -54 q-4 -2 -6 -6q-4 -8 -4 -18z" />
+<glyph unicode="[" horiz-adv-x="1364" d="M1000 626q22 -22 22 -52t-22 -52l-486 -486q-22 -22 -51 -22t-51 22l-370 372q-22 22 -22 52t22 52l486 486q22 22 52 22t52 -22l6 -8l242 244q26 26 62 26t62 -26l238 -238q26 -26 26 -62t-26 -62l-244 -242zM992 914q-12 -14 -12 -32t12 -30l20 -22q30 -30 64 0l20 22 q14 12 14 30t-14 32l-20 22q-34 26 -64 0zM840 1068q-30 -34 0 -66l20 -18q12 -14 31 -14t31 14l22 18q26 32 0 66l-22 18q-12 14 -31 14t-31 -14zM912 542q12 12 12 29t-12 29l-308 310q-12 12 -29 12t-29 -12l-424 -424q-12 -12 -12 -29t12 -29l310 -310q12 -12 29 -12 t29 12z" />
+<glyph unicode="\" horiz-adv-x="1448" d="M638 1286q132 0 248 -51t203 -138t138 -203t51 -248q0 -134 -51 -251t-138 -204t-203 -138t-248 -51q-134 0 -250 51t-203 138t-137 204t-50 251q0 132 50 248t137 203t203 138t250 51zM638 152q102 0 191 39t157 106t107 157t39 192t-39 191t-107 155t-157 105t-191 39 t-192 -39t-157 -105t-106 -155t-39 -191t39 -192t106 -157t157 -106t192 -39zM536 748l380 176l-174 -384l-382 -176zM568 714l-140 -278l282 138z" />
+<glyph unicode="]" horiz-adv-x="1102" d="M774 758q8 -24 32 -41t54 -31l59 -27q29 -13 45 -31t14 -42t-36 -60q-40 -44 -103 -71t-129 -42t-126 -20t-96 -7l-2 -2q-4 -2 -10 -2h-24l-4 2q-32 0 -83 5t-108 18t-113 39t-98 68q-34 34 -37 59t13 42t45 31l58 29q29 15 53 32t30 43q10 36 12 88t-3 103t-17 93 t-30 60t-27 41t-5 46t25 45t67 38q76 28 139 39t101 15q2 0 6 1t6 1q4 -2 8 -1t6 1q38 -4 101 -13t139 -33q46 -16 69 -38t28 -45t-5 -46t-28 -41q-16 -18 -27 -60t-14 -93t0 -103t15 -90zM474 -204l-138 512l46 -8q20 -4 44 -7t46 -7q22 -2 50 1t52 7l56 12z" />
+<glyph unicode="^" horiz-adv-x="1016" d="M730 452q18 -6 25 -22t1 -34q-4 -16 -18 -20l-606 -368q-16 -12 -36 -2q-16 4 -24 20t-2 32q4 6 8 14l200 278l-242 94q-18 6 -25 23t-1 33l8 12q4 6 10 8l348 212l-236 90q-18 6 -25 23t-1 33q8 14 20 22l604 368q20 8 38 2q16 -6 24 -23t0 -33q-4 -2 -4 -5t-2 -5 l-200 -280l240 -94q18 -6 26 -23t0 -33q-2 -14 -18 -22l-344 -208z" />
+<glyph unicode="_" horiz-adv-x="1398" d="M954 82q-46 52 -46 132q0 44 17 83t46 68t68 46t83 17q60 0 112 -34q-44 -102 -116 -181t-164 -131zM640 1282q132 0 249 -51t204 -138t138 -204t51 -249q0 -52 -8 -101t-22 -95q-32 16 -63 25t-67 9q-56 0 -105 -21t-85 -57t-57 -84t-21 -102q0 -46 14 -84t38 -72 q-126 -58 -266 -58q-142 0 -256 56q-18 6 -44 20q-4 2 -6 4t-6 4v-2q-96 52 -168 131t-112 181l4 4q-4 2 -8 10l-7 18q-3 8 -5 18h-2q-30 92 -30 196q0 132 50 249t137 204t204 138t249 51zM272 884q10 20 4 41t-28 31q-16 10 -38 6t-32 -26q-10 -18 -6 -39t26 -33 q18 -10 40 -6t34 26zM640 1206q-22 0 -38 -17t-16 -39t16 -37t38 -15q24 0 38 15t14 37t-14 39t-38 17zM428 1040q18 12 24 34t-6 38q-12 20 -33 26t-41 -4t-23 -32t7 -40q6 -22 28 -27t44 5zM674 510q16 24 40 75t44 105t29 98t-5 52q-12 8 -48 -17t-79 -64t-82 -80 t-53 -65q-32 -46 -32 -94t34 -70q32 -22 77 -4t75 64zM924 1062q12 18 8 40t-26 32q-18 10 -39 4t-33 -26q-10 -16 -6 -38t26 -34q18 -10 39 -5t31 27zM1082 864q20 12 26 33t-4 39q-10 22 -32 26t-40 -6q-22 -10 -28 -31t4 -41q12 -22 33 -26t41 6z" />
+<glyph unicode="`" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
+<glyph unicode="a" horiz-adv-x="1144" d="M904 970l-82 -930q0 -16 -12 -28t-28 -12h-574q-16 0 -28 12t-12 28l-82 930h818zM966 1136q22 -14 22 -30v-54q0 -22 -22 -22h-334h-280h-334q-20 0 -20 22v54q0 16 20 30q20 6 38 14q16 6 31 13t21 13q4 6 11 20t13 30q8 18 12 38q4 6 9 15t19 9h180h280h180 q14 0 19 -9t9 -15q4 -20 12 -38q6 -16 13 -30t13 -20t20 -13t30 -13q18 -8 38 -14zM622 1158q0 20 -20 20h-218q-20 0 -20 -20v-14q0 -22 20 -22h218q20 0 20 22v14z" />
+<glyph unicode="b" horiz-adv-x="1200" d="M142 694q0 14 14 14h4q8 0 14 -6l34 -58l38 58q2 6 12 6h2q6 0 12 -4t6 -10v-104q0 -6 -6 -12t-12 -6q-4 0 -9 6t-5 12v66l-24 -40q-8 -8 -14 -8t-12 8l-26 38v-66q0 -4 -4 -10t-10 -6t-10 6t-4 10v106zM286 592l46 108q8 12 18 12h2q14 0 18 -12l46 -108q0 -2 2 -2l2 -2 q0 -4 -6 -10t-10 -6q-6 0 -8 4t-6 8l-8 20h-62l-8 -20q-4 -12 -14 -12q-4 0 -8 6t-4 10v4zM370 630l-20 46l-18 -46h38zM428 694q0 14 14 14h46q28 0 38 -12q12 -12 12 -30q0 -34 -26 -42l20 -26q6 -6 6 -10q0 -16 -14 -16q-8 0 -10 3t-6 7l-28 36h-24v-28q0 -6 -3 -12 t-11 -6q-6 0 -10 6t-4 12v104zM486 644q22 0 22 18q0 20 -22 20h-30v-38h30zM514 436l-28 -8h-14q-14 0 -25 9t-11 23q0 30 26 34l52 14q12 4 20 7t18 3q18 0 29 -12t11 -28v-292q0 -18 -11 -30t-29 -12q-16 0 -27 12t-11 30v250zM682 206l136 236h-138q-16 0 -26 10t-10 26 t10 25t26 9h196q34 0 34 -34q0 -22 -10 -42l-146 -262q-16 -30 -36 -30q-18 0 -31 11t-13 27q0 4 3 12t5 12zM362 1026q0 -40 -27 -67t-67 -27t-66 27t-26 67v160q0 40 26 68t66 28t67 -28t27 -68v-160zM916 1026q0 -40 -26 -67t-66 -27t-68 27t-28 67v160q0 40 28 68t68 28 t66 -28t26 -68v-160zM1018 1094h-34v-90q0 -34 -13 -63t-35 -50t-51 -34t-61 -13q-68 0 -113 47t-45 113v90h-10h-226h-2v-90q0 -66 -47 -113t-113 -47q-64 0 -111 47t-47 113v90h-32q-34 0 -56 -23t-22 -55v-938q0 -34 22 -56t56 -22h940q30 0 53 22t23 56v938q0 32 -23 55 t-53 23zM994 170q0 -24 -18 -41t-42 -17h-772q-24 0 -42 17t-18 41v532q0 24 18 41t42 17h772q24 0 42 -17t18 -41v-532z" />
+<glyph unicode="c" horiz-adv-x="1466" d="M544 644q0 -18 1 -29t5 -20t14 -19l28 -28q36 -42 92 -44h7q52 0 93 26q6 0 12 6t6 12l230 222l58 58q44 42 44 104t-44 106t-106 44t-104 -44l-20 -18q-52 22 -106 23h-6q-51 0 -100 -15l144 144q44 44 99 65t113 21t113 -21t99 -65l8 -8q44 -44 65 -98t21 -109 t-21 -109t-65 -98l-192 -192l-144 -144q-58 -58 -138 -78q-38 -10 -76 -10q-41 0 -82 12q-30 8 -64 28t-62 48v10q-28 28 -36 44t-22 52zM792 626q0 36 -6 53t-42 53t-92 41q-8 1 -17 1q-46 0 -83 -24q-6 -8 -12 -10t-6 -8l-230 -222l-58 -66q-44 -44 -44 -106t44 -106 t106 -44t104 44l20 20q52 -16 106 -19h13q47 0 93 19l-144 -154q-44 -40 -99 -60t-113 -20t-113 20t-99 60l-8 18q-44 40 -66 93t-22 110t22 112t66 99l192 192l144 144q58 58 138 73q32 6 65 6q47 0 93 -13q30 -6 64 -25t62 -41v-10q14 -14 22 -26t14 -24t10 -25t12 -31z " />
+<glyph unicode="d" horiz-adv-x="1072" d="M906 778v-676q0 -42 -30 -72t-72 -30h-638q-44 0 -74 30t-30 72v676h844zM336 626q0 46 -44 46q-40 0 -40 -46v-464q0 -18 11 -32t29 -14q20 0 32 14t12 32v464zM534 626q0 46 -44 46q-16 0 -30 -12t-14 -34v-464q0 -18 14 -32t30 -14q20 0 32 14t12 32v464zM718 626 q0 22 -14 34t-30 12q-44 0 -44 -46v-464q0 -18 12 -32t32 -14q16 0 30 14t14 32v464zM968 864h-966v162q0 24 17 43t43 19h162v162q0 14 9 24t23 10h440q16 0 25 -10t9 -24v-162h178q24 0 42 -19t18 -43v-162zM642 1152q0 14 -10 23t-24 9h-262q-14 0 -24 -9t-10 -23v-64 h330v64z" />
+<glyph unicode="e" horiz-adv-x="1430" d="M264 180q56 4 93 35q33 28 33 76v11q-4 54 -45 79q-35 22 -80 22q-7 0 -15 -1q-54 -4 -91 -34q-33 -27 -33 -75v-11q4 -54 44 -80q35 -23 80 -23q7 0 14 1zM228 754q54 4 90 35q32 28 32 74v11q-4 56 -44 80q-34 21 -79 21q-7 0 -15 -1q-56 -4 -93 -33q-33 -26 -33 -74 v-11q4 -56 45 -81q35 -22 82 -22q7 0 15 1zM204 1058q10 1 20 1q77 0 136 -42q66 -47 76 -137q2 -46 -16 -78t-46 -64l142 -66l512 348q44 25 89 25l31 -3q60 -12 124 -50l-612 -388l658 -302q-58 -46 -116 -65q-26 -8 -52 -8q-33 0 -68 13l-556 276l-130 -84q34 -26 53 -53 t23 -73q1 -11 1 -21q0 -77 -53 -126q-60 -55 -148 -61q-13 -1 -26 -1q-75 0 -132 40q-66 47 -72 137q-1 8 -1 17q0 79 53 128q58 55 146 61l150 104l-170 86q-86 0 -149 46t-67 132q-1 8 -1 17q0 79 53 129q58 56 148 62z" />
+<glyph unicode="f" horiz-adv-x="1258" d="M966 1288q46 0 77 -31t31 -77v-1070q0 -46 -31 -77t-77 -31h-856q-46 0 -77 31t-31 77v1070q0 46 31 77t77 31h856zM862 592h-218v374h-212v-374h-214l322 -320z" />
+<glyph unicode="g" horiz-adv-x="864" d="M686 1084v-738q0 -44 -15 -104t-54 -114t-105 -91t-168 -37t-169 37t-106 91t-55 114t-16 104v688q0 4 4 40t28 80q62 126 214 126q76 0 131 -32t83 -94q24 -44 28 -80t4 -40v-638q0 -22 -8 -49t-25 -49t-45 -37t-68 -15q-42 0 -70 15t-46 37t-26 49t-8 49v442 q0 22 14 36t36 14t36 -14t14 -36v-442q0 -16 10 -33t40 -17q28 0 37 17t9 33v638q0 8 -3 33t-17 50t-44 45t-82 20q-88 0 -128 -70q-8 -16 -11 -30l-5 -24q-2 -12 -2 -24v-688q0 -10 5 -50t28 -83t73 -78t140 -35t140 37t74 82t28 84t4 43v738q0 22 14 36t32 14q22 0 36 -14 t14 -36z" />
+<glyph unicode="h" horiz-adv-x="1460" d="M342 170q0 -68 -50 -118t-122 -50q-36 0 -68 14t-55 37t-37 53t-14 64q0 72 51 123t123 51t122 -51t50 -123zM840 12h-228q0 120 -46 229t-132 195q-86 90 -195 136t-229 46v228q170 -4 321 -71t263 -179t178 -263t68 -321zM1282 12h-228q-2 216 -84 406t-224 332 t-331 225t-405 85v224q174 -2 336 -48t302 -130t256 -200t199 -257t130 -302t49 -335z" />
+<glyph unicode="i" horiz-adv-x="1412" d="M644 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM766 814q0 62 -70 62q-32 0 -84 -34q-28 -20 -54 -42t-54 -54q-52 -58 -52 -70v-16 q2 -4 8 -5t10 -1q8 0 36 36q76 90 106 90q10 0 10 -16q0 -10 -5 -27t-19 -43l-162 -326q-14 -34 -22 -60t-8 -46q0 -64 72 -64q36 0 84 30q24 22 51 44t57 50q28 30 40 50t12 24q0 12 -2 16q-8 10 -16 10q-4 0 -46 -44q-78 -88 -96 -88q-10 0 -10 18q0 10 5 27t19 45 l162 326q28 62 28 108zM854 1022q0 34 -28 62q-24 24 -58 24q-16 0 -32 -6t-26 -18q-24 -24 -24 -62q0 -32 24 -56q10 -12 26 -17t32 -5q36 0 58 22q28 28 28 56z" />
+<glyph unicode="j" d="M288 1038v236h804l6 -236h-810zM1334 198h-162v372h-958v-372h-156q-28 0 -45 19t-17 43v640q0 28 17 49t45 21h1276q28 0 45 -21t17 -49v-640q0 -24 -17 -43t-45 -19zM444 4l-150 150v342h804v-492h-654z" />
+<glyph unicode="k" horiz-adv-x="1072" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41z" />
+<glyph unicode="l" horiz-adv-x="1112" d="M1002 646v-630q0 -18 -14 -30t-32 -12h-598q-20 0 -33 12t-13 30v884q0 16 13 29t33 13h344zM272 1034q-22 0 -38 -17t-16 -41v-372h-182q-14 0 -23 10t-9 24v614q0 10 9 19t23 9h240l208 -208v-38h-212z" />
+<glyph unicode="m" horiz-adv-x="1126" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41zM448 148q74 0 141 28t116 78t78 115t29 141q0 74 -29 141t-78 116t-116 78t-141 29q-76 0 -141 -29t-115 -78t-78 -116t-28 -141q0 -76 28 -141t78 -115t115 -78 t141 -28zM198 510q0 38 11 71t29 63l344 -344q-30 -18 -63 -29t-71 -11q-52 0 -97 20t-79 54t-54 79t-20 97zM698 510q0 -76 -38 -134l-346 346q58 38 134 38q52 0 98 -20t80 -54t53 -79t19 -97z" />
+<glyph unicode="n" d="M650 206l-276 276l276 278q12 10 12 28t-12 28l-136 138q-15 13 -30 13l-30 -13l-296 -298q-4 0 -5 -2t-5 -4l-138 -136q-14 -14 -10 -32q-4 -16 10 -30l138 -136q6 -6 10 -6l296 -300q15 -12 30 -12l30 12l136 140q12 10 12 28t-12 28zM808 206l276 276l-276 278 q-10 10 -10 28t10 28l138 138q14 14 28.5 14t29.5 -14l296 -298q4 0 5 -2t5 -4l138 -136q14 -14 10 -32q4 -16 -10 -30l-138 -136q-6 -6 -10 -6l-296 -300q-15 -12 -30 -12l-30 12l-136 140q-10 10 -10 28t10 28z" />
+<glyph unicode="o" horiz-adv-x="1340" d="M1098 1280q74 0 126 -52t52 -126q0 -76 -50 -126v-2l-154 -156q20 -18 29 -45t9 -56t-9 -56t-29 -47l-154 -154l-104 102l-510 -514l-208 -48q-40 0 -70 30t-30 70l48 208l512 514l-100 100l154 154q20 20 47 29t56 9t56 -9t45 -29l156 154q26 26 59 38t69 12z" />
+<glyph unicode="p" horiz-adv-x="1466" d="M802 1238l40 -79q22 -45 47 -87t51 -76t50 -42q14 8 24 18q4 60 44 98t90 44q8 1 16 1q41 0 81 -26q47 -31 63 -109q6 -32 6 -55q0 -16 -3 -29q-7 -30 -26 -47t-47 -23t-59 -10t-60 -10t-51 -22q34 -76 73 -152l75 -148q-26 -16 -60 -39l-70 -46q-36 -23 -73 -42t-69 -25 q-10 4 -12 14t-6 14q4 26 4 50q0 39 -10 73q-16 55 -52 80q-24 16 -55 16q-16 0 -34 -4q-53 -13 -117 -81q-36 -38 -39 -69t13 -58t44 -48l54 -39l45 -31q19 -13 15 -23l-129 -77q-63 -37 -127 -79l-91 142l-91 142q-6 -4 -16 -7t-18 -5q-14 -48 -51 -92t-83 -59 q-17 -6 -34 -6q-29 0 -57 16q-45 25 -75 113q-14 42 -14 72q0 21 7 37q17 37 54 55t85 22t88 8q4 2 9 6t9 8l-80 150l-80 148l136 81l136 83q16 -20 21 -46t6 -54l2 -56q1 -28 11 -49t34 -34q21 -11 58 -11h12q44 0 84 26t64 65q26 42 26 90q0 42 -28 79q-18 10 -38 16 l-38 11q-18 5 -31 11t-17 18q-10 26 -3 26t7 8l132 77z" />
+<glyph unicode="q" horiz-adv-x="1620" d="M744 1280q154 0 290 -14t237 -38t159 -57t58 -71v-4q0 -44 -36 -94t-91 -103t-123 -109l-131 -109q-63 -53 -114 -104t-73 -95v-392q0 -38 -52 -63t-124 -25t-124 25t-52 63v392q-22 44 -72 95t-113 104l-131 109q-68 56 -124 109t-92 103t-36 94v4q0 38 58 71t159 57 t237 38t290 14zM744 1204q-118 0 -222 -8t-182 -22t-123 -32t-45 -38q0 -22 45 -39t123 -31t182 -21t222 -7t222 7t182 21t123 31t45 39q0 20 -45 38t-123 32t-182 22t-222 8z" />
+<glyph unicode="r" horiz-adv-x="1442" d="M1254 184q30 -32 30 -76t-30 -76q-16 -16 -37 -24t-39 -8q-46 0 -78 32l-308 308q-2 4 -2 6t-4 6q-62 -40 -132 -62t-146 -22q-106 0 -198 40t-160 109t-108 162t-40 197q0 106 40 199t108 162t160 109t198 40t198 -40t161 -109t109 -162t40 -199q0 -76 -21 -146 t-59 -128q4 -4 5 -6t5 -6zM508 428q72 0 136 28t111 75t74 110t27 135t-27 135t-74 110t-111 75t-136 28t-135 -28t-111 -75t-75 -110t-27 -135t27 -135t75 -110t111 -75t135 -28z" />
+<glyph unicode="s" horiz-adv-x="1152" d="M672 840q102 -150 182 -282l67 -116q33 -58 59 -112t42 -98t16 -74q0 -58 -25 -89t-53 -47q-36 -18 -78 -20h-368h-364q-44 2 -78 20q-30 16 -54 47t-24 89q0 30 15 74t41 98t59 112l67 116q80 132 184 282v346h-22q-20 0 -34 13t-14 35q0 18 14 31t34 13h358 q20 0 33 -13t13 -31q0 -22 -13 -35t-33 -13h-24v-346z" />
+<glyph unicode="t" horiz-adv-x="1466" d="M1308 1022q30 0 52 -23t22 -53v-876q0 -32 -22 -52t-52 -20h-1232q-32 0 -54 20t-22 52v876q0 30 22 53t54 23h226v218q0 16 12 29t28 13h698q18 0 30 -13t12 -29v-218h226zM412 1022h560v96q0 16 -13 28t-29 12h-478q-16 0 -28 -12t-12 -28v-96z" />
+<glyph unicode="u" d="M1360 234h-984q-28 0 -47 20t-19 46l-82 614l-204 176q-24 18 -24 48q0 28 21 47t45 19q26 0 44 -14h4l200 -172l1152 -94q28 0 49 -19t21 -43l-108 -562q0 -26 -20 -46t-48 -20zM1332 0q-36 0 -63 28q-24 24 -24 58q0 5 1 10q0 34 28 59t62 25q36 -4 61 -29t25 -65 q0 -36 -27 -61t-63 -25zM386 0q-36 0 -64 27t-28 63t28 63t64 27q34 0 62 -27t28 -63t-28 -63t-62 -27z" />
+<glyph unicode="v" horiz-adv-x="1346" d="M762 538v-300l160 -128v-108l-268 108l-268 -108v108l160 128v300l-482 -162v108l482 322v374q2 30 14 54q10 20 32 37t62 17t62 -17t32 -37q12 -24 14 -54v-374l480 -322v-108z" />
+<glyph unicode="w" horiz-adv-x="1172" d="M1004 984q-2 -2 -2 -6v-148q2 -6 2 -20q-2 -2 -2 -4v-2v-2q-6 -42 -45 -81t-113 -71q-76 -34 -169 -50t-191 -16h-21q-85 0 -166 14q-91 16 -163 50q-72 32 -107 73q-31 37 -31 74q0 4 1 8.5t1 8.5v146q-2 6 -2 20q2 6 2 18q18 -76 136 -128q72 -34 163 -49t187 -15 q98 0 191 17t169 49q70 30 108 67t48 77q4 -20 4 -30zM1004 674q-2 0 -2 -4v-150q2 -4 2 -18q-2 -2 -2 -8v-2q-6 -40 -45 -79t-113 -71q-76 -34 -169 -51t-191 -17h-20q-85 0 -167 15q-91 17 -163 49q-72 34 -107 75q-31 37 -31 74q0 4 1 8.5t1 8.5v144q-2 8 -2 22q2 4 2 8 v8q18 -72 136 -128q72 -32 163 -48t187 -16q98 2 191 18t169 50q70 30 108 67t48 77q2 -8 3 -15t1 -17zM1004 356q-2 -2 -2 -8v-146q2 -8 2 -20q-2 -2 -2 -6v-4q-6 -40 -45 -79t-113 -69q-76 -34 -169 -51t-191 -19q-96 0 -187 15t-163 49q-138 63 -138 153q0 3 1 7t1 6v146 q-2 6 -2 20q2 6 2 18q10 -34 43 -68t93 -60q72 -34 163 -49t187 -15q98 0 191 17t169 51q70 30 108 67t48 75q4 -20 4 -30zM134 962q-118 56 -136 128q10 40 49 77t107 67q76 34 170 50t192 18q96 0 188 -15t162 -49q118 -54 134 -128q-20 -82 -156 -142q-76 -34 -169 -51 t-191 -17h-21q-85 0 -166 14q-91 16 -163 48z" />
+<glyph unicode="x" horiz-adv-x="1358" d="M794 364h11q101 0 171 -36q74 -38 126 -96t86 -124l60 -116h-628h-628l60 116q34 66 85 124t124 96q69 36 168 36h11q34 0 54 23t20 55q0 10 -15 29t-31 37q-20 22 -44 46q-36 38 -64 85t-46 98t-27 102t-9 97q0 70 26 132t72 109t108 74t134 27h2q70 0 132 -27t108 -74 t73 -109t27 -132q0 -46 -9 -97t-27 -102t-45 -98t-63 -85q-26 -24 -46 -46q-16 -18 -30 -37t-14 -29q0 -32 20 -55t48 -23z" />
+<glyph unicode="y" horiz-adv-x="1298" d="M630 844v-210h426q14 0 14 -12v-288q0 -12 -14 -12h-426v-304q0 -20 -20 -20h-102q-20 0 -20 20v304h-300v-2q-18 -18 -36 2l-140 136q-10 10 -10 21t8 17l142 144q8 4 18 4t18 -8v-2h300v210h-384q-14 0 -14 12v288q0 12 14 12h384v108q0 18 20 18h102q20 0 20 -18v-108 h340v4q22 18 40 0l138 -142q9 -7 9 -16q0 -10 -9 -22l-138 -138q-8 -8 -19 -8t-19 8v2h-342z" />
+<glyph unicode="z" horiz-adv-x="1072" d="M724 942l142 -228q-74 6 -152 14q-66 6 -143 11t-147 5q-102 0 -175 -11t-107 -17v-694q0 -20 -20 -20h-100q-20 0 -20 20v1246q0 20 20 20h100q20 0 20 -20v-88q34 8 107 20t175 12q70 0 147 -6t143 -12l152 -16z" />
+<glyph unicode="{" horiz-adv-x="638" d="M452 8l-226 226l-226 -226v1268h452v-1268z" />
+<glyph unicode="|" horiz-adv-x="822" d="M540 1266q36 -10 58 -38t24 -64q38 8 71 -6t53 -48q18 -34 13 -70t-31 -62q32 -22 46 -55t4 -69q-10 -38 -38 -60t-66 -24q10 -34 -4 -69t-48 -53q-34 -20 -69 -15t-63 29q-22 -30 -55 -45t-69 -5t-59 40t-25 66q-34 -8 -69 6t-53 48q-20 32 -15 69t29 63q-30 20 -44 53 t-4 73q10 36 39 57t65 23q-8 36 6 70t48 54q32 20 69 14t63 -30q20 32 54 45t70 3zM492 1086q-60 16 -114 -15t-70 -91q-16 -62 15 -115t93 -69q30 -8 59 -3t54 19t43 37t26 53q16 60 -15 114t-91 70zM254 610q2 -36 24 -66t60 -40q36 -10 68 5t54 45q16 -16 35 -22t39 -8 l-144 -538l-80 139q-28 49 -59 60t-80 -16t-143 -87l144 540q42 -22 82 -12z" />
+<glyph unicode="}" horiz-adv-x="1406" d="M662 -52q-134 0 -250 56l250 532l92 -226l37 -92l36 -88l32 -78q15 -36 25 -58q-104 -46 -222 -46zM338 120q-74 36 -136 90t-106 123t-69 150t-25 169q0 70 17 136t47 124l522 -260zM932 188q-10 22 -24 57l-31 77l-36 88l-37 92l-92 224l-522 264q36 72 90 131t121 102 t146 67t165 24q120 0 227 -46t186 -126t125 -187t46 -229q0 -92 -27 -175t-75 -153t-115 -124t-147 -86z" />
+<glyph unicode="~" d="M1328 686q32 -34 34 -80t-30 -74h-8v-484q0 -22 -16 -38t-38 -16h-238v454q0 32 -23 55t-53 23h-224q-32 0 -54 -23t-22 -55v-454h-546q-20 0 -37 16t-17 38v478h-8q-32 34 -34 79t30 73l568 572q34 28 79 27t75 -35z" />
+<glyph unicode="&#xad;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
+<glyph unicode="&#xc9;" horiz-adv-x="1502" />
+<glyph unicode="&#xd1;" horiz-adv-x="1030" d="M718 1178q-22 -116 4 -224t61 -212t59 -205t-4 -203q-22 -80 -73 -142t-118 -99t-147 -47t-162 12t-144 72t-101 117t-50 145t11 158q18 68 58 124t90 105t107 95t110 97t98 109t73 132l4 14q8 28 31 41t51 5q26 -8 39 -31t7 -49zM418 744q26 4 44 24t16 48q-4 28 -25 46 t-49 14t-45 -24t-15 -48t24 -45t50 -15zM306 594q16 12 20 34t-8 40q-16 22 -39 28t-47 -10q-28 -16 -50 -56t-36 -90q-12 -52 -9 -102t19 -74q14 -24 37 -29t45 7q16 10 24 32t0 40q-16 46 -5 95t49 85z" />
+<glyph unicode="&#xe1;" horiz-adv-x="1394" d="M252 152q18 22 18 50t-18 50q-22 18 -52 18t-48 -18q-22 -22 -22 -50t22 -50q18 -22 48 -22t52 22zM64 294l18 18l500 500q-32 92 -10 187t92 165q62 62 141 88t159 12l-200 -200l66 -234l234 -66l200 200q10 -80 -14 -158t-86 -136q-70 -76 -167 -98t-185 10l-500 -500 l-18 -18q-48 -48 -114 -48t-116 48q-48 50 -48 118t48 112z" />
+<glyph unicode="&#xe9;" horiz-adv-x="1376" d="M626 1022q-80 0 -149 -30t-120 -81t-81 -120t-30 -149q0 -82 30 -152t81 -121t120 -81t149 -30t151 30t122 81t81 121t30 152q0 80 -30 149t-81 120t-122 81t-151 30zM198 580l-168 14q-10 0 -20 12t-10 36q0 22 10 33t20 11l168 16v-122zM1054 702l170 -16q46 0 46 -44 q0 -14 -12 -31t-34 -17l-170 -14q14 22 14 61t-14 61zM688 214l-14 -170q0 -12 -10 -29t-20 -17q-24 0 -36 17t-12 29l-30 170q12 -6 30 -10t37 -4t34 4t21 10zM920 1008l104 92q0 12 17 12t31 -12q10 -12 10 -32t-10 -30l-92 -106q-12 22 -31 43t-29 33zM368 304l-108 -90 q-22 -28 -44 0q-14 10 -14 29t14 31l74 108q26 -56 78 -78zM994 382l78 -108q20 -24 0 -46q0 -10 -18 -10t-30 10l-104 90q22 0 43 26zM290 932l-92 106q-24 22 0 46q26 22 48 0l104 -92q-6 0 -16 -7t-20 -17t-17 -20t-7 -16zM566 1070l16 168q0 22 17 34t27 12q48 0 48 -46 l14 -168q-22 10 -61 10t-61 -10z" />
+<glyph unicode="&#xed;" horiz-adv-x="1400" d="M638 1272q134 0 251 -49t203 -135t135 -203t49 -251t-49 -250t-135 -202t-203 -135t-251 -49t-250 49t-202 135t-135 202t-49 250t49 251t135 203t202 135t250 49zM638 100q114 0 212 41t170 112t113 169t41 212t-41 212t-113 170t-170 113t-212 41v-1070z" />
+<glyph unicode="&#xf1;" d="M1222 972q22 0 37 -17t15 -39v-232q0 -22 -15 -37t-37 -15h-948q-22 0 -37 15t-15 37v232q0 22 15 39t37 17h948zM408 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM882 740q-6 14 -8 29t-2 31q0 40 10 62h-268l8 -29 q4 -15 4 -33q0 -16 -4 -31l-8 -29h268zM1088 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM1362 1172q52 0 87 -36t35 -86v-868q0 -52 -35 -87t-87 -35h-1236q-52 0 -88 35t-36 87v868q0 50 36 86t88 36h1236zM240 130q18 0 30 13 t12 31t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM368 242q18 0 30 12t12 30q0 16 -12 28t-30 12t-30 -12t-12 -28q0 -18 12 -30t30 -12zM1140 242q16 0 28 12t12 30q0 16 -12 28t-28 12q-18 0 -30 -12t-12 -28q0 -18 12 -30t30 -12zM1266 130q18 0 30 13t12 31 t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM1396 1020q0 22 -15 39t-39 17h-1190q-22 0 -37 -17t-15 -39v-568q0 -22 15 -37t37 -15h1190q24 0 39 15t15 37v568z" />
+<glyph unicode="&#xf3;" horiz-adv-x="1418" d="M304 1284q30 0 53 -22t23 -52v-226q0 -32 -23 -54t-53 -22h-226q-32 0 -55 22t-23 54v226q0 30 23 52t55 22h226zM756 1284q32 0 53 -22t21 -52v-226q0 -32 -21 -54t-53 -22h-228q-30 0 -52 22t-22 54v226q0 30 22 52t52 22h228zM1210 1284q32 0 53 -22t21 -52v-226 q0 -32 -21 -54t-53 -22h-226q-32 0 -54 22t-22 54v226q0 30 22 52t54 22h226zM304 830q30 0 53 -21t23 -53v-228q0 -30 -23 -52t-53 -22h-226q-32 0 -55 22t-23 52v228q0 32 23 53t55 21h226zM756 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-228q-30 0 -52 22 t-22 52v228q0 32 22 53t52 21h228zM1210 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-226q-32 0 -54 22t-22 52v228q0 32 22 53t54 21h226zM304 380q30 0 53 -23t23 -55v-228q0 -28 -23 -51t-53 -23h-226q-32 0 -55 23t-23 51v228q0 32 23 55t55 23h226zM756 380 q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-228q-30 0 -52 23t-22 51v228q0 32 22 55t52 23h228zM1210 380q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-226q-32 0 -54 23t-22 51v228q0 32 22 55t54 23h226z" />
+<glyph unicode="&#xf6;" horiz-adv-x="1412" d="M1214 1282q32 0 53 -22t21 -54v-226q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v226q0 32 21 54t53 22h906h112h114zM1214 828q32 0 53 -21t21 -51v-228q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v228q0 30 21 51t53 21h906h112h114z M1214 378q32 0 53 -23t21 -55v-224q0 -30 -21 -53t-53 -23h-114h-112h-906q-32 0 -53 23t-21 53v224q0 32 21 55t53 23h906h112h114z" />
+<glyph unicode="&#xfa;" horiz-adv-x="1370" d="M1248 698q24 -24 24 -57t-24 -57l-552 -552q-26 -26 -58 -26t-58 26l-554 552q-22 24 -22 57t22 57l554 552q26 24 58 24t58 -24zM720 1104q10 8 10 21t-10 23l-62 60q-22 20 -42 0l-60 -60q-10 -10 -10 -23t10 -21l60 -60q10 -10 21 -10t21 10zM558 944q20 20 0 42 l-58 60q-24 24 -44 0l-58 -60q-12 -10 -12 -21t12 -21l58 -62q20 -20 44 0zM404 786q22 22 0 46l-62 56q-22 20 -42 0l-60 -56q-22 -24 0 -46l60 -58q10 -10 21 -10t21 10zM240 624q20 20 0 42l-58 62q-10 10 -23 10t-23 -10l-58 -62q-12 -10 -12 -21t12 -21l58 -60 q10 -10 23 -10t23 10zM1196 624q10 10 10 23t-10 23l-56 58q-10 10 -23 10t-23 -10l-58 -58q-12 -10 -12 -23t12 -23l58 -58q10 -10 23 -10t23 10zM1036 466q10 8 10 20t-10 22l-58 62q-10 6 -23 6t-23 -6l-58 -62q-10 -10 -10 -22t10 -20l58 -60q10 -10 23 -10t23 10z M880 308q10 10 10 23t-10 23l-60 58q-22 20 -42 0l-62 -58q-10 -10 -10 -23t10 -23l62 -58q10 -10 21 -10t21 10zM716 146q20 24 0 44l-58 60q-10 10 -22 10t-22 -10l-58 -60q-20 -20 0 -44l58 -60q10 -10 22 -10t22 10zM738 442q10 10 10 23t-10 23l-258 258q-10 10 -23 10 t-23 -10l-226 -226q-20 -20 0 -44l260 -260q10 -10 22 -10t22 10zM1060 764q8 10 8 23t-8 19l-262 262q-22 20 -42 0l-224 -224q-10 -10 -10 -23t10 -23l258 -258q10 -10 23 -10t23 10z" />
+<glyph unicode="&#x2000;" horiz-adv-x="886" />
+<glyph unicode="&#x2001;" horiz-adv-x="1774" />
+<glyph unicode="&#x2002;" horiz-adv-x="886" />
+<glyph unicode="&#x2003;" horiz-adv-x="1774" />
+<glyph unicode="&#x2004;" horiz-adv-x="590" />
+<glyph unicode="&#x2005;" horiz-adv-x="442" />
+<glyph unicode="&#x2006;" horiz-adv-x="294" />
+<glyph unicode="&#x2007;" horiz-adv-x="294" />
+<glyph unicode="&#x2008;" horiz-adv-x="220" />
+<glyph unicode="&#x2009;" horiz-adv-x="354" />
+<glyph unicode="&#x200a;" horiz-adv-x="98" />
+<glyph unicode="&#x2010;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
+<glyph unicode="&#x2011;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
+<glyph unicode="&#x2012;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
+<glyph unicode="&#x2013;" horiz-adv-x="1094" d="M842 1288q38 0 71 -18t58 -50t39 -75t14 -93v-822q0 -48 -14 -91t-39 -75t-58 -51t-71 -19h-655q-38 0 -72 19t-59.5 51t-40.5 75t-15 91v822q0 50 15 93t40.5 75t59.5 50t72 18h655zM842 756h-234h-421v-230h421h234v230z" />
+<glyph unicode="&#x2014;" horiz-adv-x="2118" d="M1684 1288q76 0 142.5 -18t115.5 -50t77.5 -75t28.5 -93v-822q0 -48 -28.5 -91t-77.5 -75t-115.5 -51t-142.5 -19h-1310q-76 0 -144.5 19t-119 51t-80.5 75t-30 91v822q0 50 30 93t80.5 75t119 50t144.5 18h1310zM1684 756h-468h-842v-230h842h468v230z" />
+<glyph unicode="&#x2018;" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
+<glyph unicode="&#x2019;" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
+<glyph unicode="&#x201c;" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" />
+<glyph unicode="&#x201d;" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" />
+<glyph unicode="&#x202f;" horiz-adv-x="354" />
+<glyph unicode="&#x205f;" horiz-adv-x="442" />
+<glyph unicode="&#xe000;" horiz-adv-x="1280" d="M0 1280h1280v-1280h-1280v1280z" />
+</font>
+</defs></svg> \ No newline at end of file
diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.ttf b/addons/web/static/src/font/mnmliconsv21-webfont.ttf
new file mode 100755
index 00000000000..c38e59cf138
--- /dev/null
+++ b/addons/web/static/src/font/mnmliconsv21-webfont.ttf
Binary files differ
diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.woff b/addons/web/static/src/font/mnmliconsv21-webfont.woff
new file mode 100755
index 00000000000..3ddf14fb814
--- /dev/null
+++ b/addons/web/static/src/font/mnmliconsv21-webfont.woff
Binary files differ
diff --git a/addons/web/static/src/img/form_sheetbg.png b/addons/web/static/src/img/form_sheetbg.png
new file mode 100644
index 00000000000..d54b6302d5a
--- /dev/null
+++ b/addons/web/static/src/img/form_sheetbg.png
Binary files differ
diff --git a/addons/web/static/src/img/form_steps.png b/addons/web/static/src/img/form_steps.png
new file mode 100644
index 00000000000..0485c5977aa
--- /dev/null
+++ b/addons/web/static/src/img/form_steps.png
Binary files differ
diff --git a/addons/web/static/src/img/list_empty_arrow.png b/addons/web/static/src/img/list_empty_arrow.png
new file mode 100644
index 00000000000..b0b1372adfd
--- /dev/null
+++ b/addons/web/static/src/img/list_empty_arrow.png
Binary files differ
diff --git a/addons/web/static/src/img/topbar-avatar.png b/addons/web/static/src/img/topbar-avatar.png
deleted file mode 100644
index a2c70ea7d49..00000000000
--- a/addons/web/static/src/img/topbar-avatar.png
+++ /dev/null
Binary files differ
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index 282435f2a71..1251975f41c 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -47,26 +47,15 @@
/*---------------------------------------------------------
* OpenERP Web web module split
*---------------------------------------------------------*/
-
-/**
- * @namespace
- */
-openerp.web = function(instance) {
- openerp.web.core(instance);
- if (openerp.web.dates) {
- openerp.web.dates(instance);
- }
- openerp.web.formats(instance);
- openerp.web.chrome(instance);
- openerp.web.data(instance);
- var files = ["views","search","list","form", "page","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"];
+openerp.web = function(session) {
+ var files = ["corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"];
for(var i=0; i<files.length; i++) {
if(openerp.web[files[i]]) {
- openerp.web[files[i]](instance);
+ openerp.web[files[i]](session);
}
}
- instance.log = function() {
- if (instance.connection.debug && window.console) {
+ session.log = function() {
+ if (session.connection.debug && window.console) {
console.log.apply(console, arguments);
}
}
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 7743e2b5777..f1d3faef367 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -1,18 +1,17 @@
/*---------------------------------------------------------
* OpenERP Web chrome
*---------------------------------------------------------*/
-openerp.web.chrome = function(openerp) {
-var QWeb = openerp.web.qweb,
- _t = openerp.web._t;
+openerp.web.chrome = function(instance) {
+var QWeb = instance.web.qweb,
+ _t = instance.web._t;
-openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.Notification# */{
+instance.web.Notification = instance.web.Widget.extend({
template: 'Notification',
-
init: function() {
this._super.apply(this, arguments);
- openerp.notification = this;
+ // move to instance.web.notification
+ instance.notification = this;
},
-
start: function() {
this._super.apply(this, arguments);
this.$element.notify({
@@ -42,24 +41,16 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.
text: text
}, opts);
}
-
});
-openerp.web.dialog = function(element) {
+instance.web.dialog = function(element) {
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
result.dialog("widget").addClass("openerp");
return result;
}
-openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{
+instance.web.Dialog = instance.web.Widget.extend({
dialog_title: "",
- /**
- * @constructs openerp.web.Dialog
- * @extends openerp.web.OldWidget
- *
- * @param parent
- * @param options
- */
init: function (parent, options, content) {
var self = this;
this._super(parent);
@@ -92,7 +83,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
if (this.dialog_options.autoOpen) {
this.open();
} else {
- openerp.web.dialog(this.$element, this.get_options());
+ instance.web.dialog(this.$element, this.get_options());
}
},
get_options: function(options) {
@@ -128,10 +119,10 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
open: function(options) {
// TODO fme: bind window on resize
if (this.template) {
- this.$element.html(this.render());
+ this.$element.html(this.renderElement());
}
var o = this.get_options(options);
- openerp.web.dialog(this.$element, o).dialog('open');
+ instance.web.dialog(this.$element, o).dialog('open');
if (o.height === 'auto' && o.max_height) {
this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
@@ -149,14 +140,13 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
//openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height());
},
destroy: function () {
- // Destroy widget
this.close();
this.$element.dialog('destroy');
this._super();
}
});
-openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
+instance.web.CrashManager = instance.web.CallbackEnabled.extend({
on_rpc_error: function(error) {
if (error.data.fault_code) {
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
@@ -172,7 +162,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
}
},
on_managed_error: function(error) {
- openerp.web.dialog($('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>'), {
+ instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -182,14 +172,14 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
on_traceback: function(error) {
var self = this;
var buttons = {};
- if (openerp.connection.openerp_entreprise) {
+ if (instance.connection.openerp_entreprise) {
buttons[_t("Send OpenERP Enterprise Report")] = function() {
var $this = $(this);
var issuename = $('#issuename').val();
var explanation = $('#explanation').val();
var remark = $('#remark').val();
// Call the send method from server to send mail with details
- new openerp.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){
+ new instance.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){
if (result === false) {
alert('There was a communication error.')
} else {
@@ -205,7 +195,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
$(this).dialog("close");
};
}
- var dialog = new openerp.web.Dialog(this, {
+ var dialog = new instance.web.Dialog(this, {
title: "OpenERP " + _.str.capitalize(error.type),
width: '80%',
height: '50%',
@@ -213,19 +203,12 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
min_height: '600px',
buttons: buttons
}).open();
- dialog.$element.html(QWeb.render('CrashManagerError', {session: openerp.connection, error: error}));
+ dialog.$element.html(QWeb.render('CrashManager.error', {session: instance.connection, error: error}));
}
});
-openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loading# */{
+instance.web.Loading = instance.web.Widget.extend({
template: 'Loading',
- /**
- * @constructs openerp.web.Loading
- * @extends openerp.web.OldWidget
- *
- * @param parent
- * @param element_id
- */
init: function(parent) {
this._super(parent);
this.count = 0;
@@ -258,10 +241,9 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.count += increment;
if (this.count > 0) {
- $(".loading",this.$element).text(_.str.sprintf(
- _t("Loading (%d)"), this.count));
- $(".loading",this.$element).show();
- this.getParent().$element.addClass('loading');
+ this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
+ this.$element.show();
+ this.getParent().$element.addClass('oe_wait');
} else {
this.count = 0;
clearTimeout(this.long_running_timer);
@@ -270,73 +252,60 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.blocked_ui = false;
$.unblockUI();
}
- $(".loading",this.$element).fadeOut();
- this.getParent().$element.removeClass('loading');
+ this.$element.fadeOut();
+ this.getParent().$element.removeClass('oe_wait');
}
}
});
-openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Database# */{
- template: "DatabaseManager",
- /**
- * @constructs openerp.web.Database
- * @extends openerp.web.OldWidget
- *
- * @param parent
- * @param element_id
- * @param option_id
- */
- init: function(parent, element_id, option_id) {
- this._super(parent, element_id);
+instance.web.DatabaseManager = instance.web.Widget.extend({
+ init: function(parent) {
+ this._super(parent);
this.unblockUIFunction = $.unblockUI;
$.validator.addMethod('matches', function (s, _, re) {
return new RegExp(re).test(s);
}, _t("Invalid database name"));
},
start: function() {
- this.$option_id = $("#oe_db_options");
-
var self = this;
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
- function(result) { self.db_list = result.db_list; },
- function (_, ev) { ev.preventDefault(); self.db_list = null; });
- var fetch_langs = this.rpc("/web/session/get_lang_list", {}, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- }
+ function(result) {
+ self.db_list = result.db_list;
+ },
+ function (_, ev) {
+ ev.preventDefault();
+ self.db_list = null;
+ });
+ var fetch_langs = this.rpc("/web/session/get_lang_list", {}).then(function(result) {
self.lang_list = result.lang_list;
});
- $.when(fetch_db, fetch_langs).then(function () {self.do_create();});
-
- this.$element.find('#db-create').click(this.do_create);
- this.$element.find('#db-drop').click(this.do_drop);
- this.$element.find('#db-backup').click(this.do_backup);
- this.$element.find('#db-restore').click(this.do_restore);
- this.$element.find('#db-change-password').click(this.do_change_password);
- this.$element.find('#back-to-login').click(function() {
- self.hide();
+ return $.when(fetch_db, fetch_langs).then(self.do_render);
+ },
+ do_render: function() {
+ var self = this;
+ self.$element.html(QWeb.render("DatabaseManager", { widget : self }));
+ self.$element.find(".oe_database_manager_menu").tabs();
+ self.$element.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
+ self.$element.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
+ self.$element.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
+ self.$element.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
+ self.$element.find("form[name=change_pwd_form]").validate({
+ messages: {
+ old_pwd: "Please enter your previous password",
+ new_pwd: "Please enter your new password",
+ confirm_pwd: {
+ required: "Please confirm your new password",
+ equalTo: "The confirmation does not match the password"
+ }
+ },
+ submitHandler: self.do_change_password
});
+ self.$element.find("#back_to_login").click(self.do_exit);
},
destroy: function () {
- this.hide();
- this.$option_id.empty();
-
- this.$element
- .find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login')
- .unbind('click')
- .end()
- .empty();
+ this.$element.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
this._super();
},
- show: function () {
- this.$element.closest(".login")
- .addClass("database_block");
- },
- hide: function () {
- this.$element.closest(".login")
- .removeClass("database_block")
- },
/**
* Converts a .serializeArray() result into a dict. Does not bother folding
* multiple identical keys into an array, last key wins.
@@ -375,7 +344,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
* @param {String} error.error message of the error dialog
*/
display_error: function (error) {
- return openerp.web.dialog($('<div>'), {
+ return instance.web.dialog($('<div>'), {
modal: true,
title: error.title,
buttons: [
@@ -383,157 +352,96 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
]
}).html(error.error);
},
- do_create: function() {
+ do_create: function(form) {
var self = this;
- self.$option_id.html(QWeb.render("Database.CreateDB", self));
- self.$option_id.find("form[name=create_db_form]").validate({
- submitHandler: function (form) {
- var fields = $(form).serializeArray();
- self.rpc("/web/database/create", {'fields': fields}, function(result) {
- if (self.db_list) {
- self.db_list.push(self.to_object(fields)['db_name']);
- self.db_list.sort();
- self.getParent().set_db_list(self.db_list);
- }
-
- var form_obj = self.to_object(fields);
- self.getParent().do_login(
- form_obj['db_name'],
- 'admin',
- form_obj['create_admin_pwd']);
- self.destroy();
- });
- }
+ var fields = $(form).serializeArray();
+ self.rpc("/web/database/create", {'fields': fields}, function(result) {
+ var form_obj = self.to_object(fields);
+ self.getParent().do_login( form_obj['db_name'], 'admin', form_obj['create_admin_pwd']);
+ self.destroy();
});
+
},
- do_drop: function() {
+ do_drop: function(form) {
var self = this;
- self.$option_id.html(QWeb.render("DropDB", self));
- self.$option_id.find("form[name=drop_db_form]").validate({
- submitHandler: function (form) {
- var $form = $(form),
- fields = $form.serializeArray(),
- $db_list = $form.find('[name=drop_db]'),
- db = $db_list.val();
-
- if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
- return;
- }
- self.rpc("/web/database/drop", {'fields': fields}, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- }
- $db_list.find(':selected').remove();
- if (self.db_list) {
- self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
- self.getParent().set_db_list(self.db_list);
- }
- self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
- });
+ var $form = $(form),
+ fields = $form.serializeArray(),
+ $db_list = $form.find('[name=drop_db]'),
+ db = $db_list.val();
+ if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
+ return;
+ }
+ self.rpc("/web/database/drop", {'fields': fields}, function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
}
+ self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
+ self.start();
});
},
- do_backup: function() {
+ do_backup: function(form) {
var self = this;
- self.$option_id
- .html(QWeb.render("BackupDB", self))
- .find("form[name=backup_db_form]").validate({
- submitHandler: function (form) {
- self.blockUI();
- self.session.get_file({
- form: form,
- success: function () {
- self.do_notify(_t("Backed"),
- _t("Database backed up successfully"));
- },
- error: openerp.webclient.crashmanager.on_rpc_error,
- complete: function() {
- self.unblockUI();
- }
- });
+ self.blockUI();
+ self.session.get_file({
+ form: form,
+ success: function () {
+ self.do_notify(_t("Backed"), _t("Database backed up successfully"));
+ },
+ error: instance.webclient.crashmanager.on_rpc_error,
+ complete: function() {
+ self.unblockUI();
}
});
},
- do_restore: function() {
+ do_restore: function(form) {
var self = this;
- self.$option_id.html(QWeb.render("RestoreDB", self));
+ self.blockUI();
+ $(form).ajaxSubmit({
+ url: '/web/database/restore',
+ type: 'POST',
+ resetForm: true,
+ success: function (body) {
+ // If empty body, everything went fine
+ if (!body) { return; }
- self.$option_id.find("form[name=restore_db_form]").validate({
- submitHandler: function (form) {
- self.blockUI();
- $(form).ajaxSubmit({
- url: '/web/database/restore',
- type: 'POST',
- resetForm: true,
- success: function (body) {
- // TODO: ui manipulations
- // note: response objects don't work, but we have the
- // HTTP body of the response~~
-
- // If empty body, everything went fine
- if (!body) { return; }
-
- if (body.indexOf('403 Forbidden') !== -1) {
- self.display_error({
- title: 'Access Denied',
- error: 'Incorrect super-administrator password'
- })
- } else {
- self.display_error({
- title: 'Restore Database',
- error: 'Could not restore the database'
- })
- }
- },
- complete: function() {
- self.unblockUI();
- self.do_notify(_t("Restored"), _t("Database restored successfully"));
- }
- });
+ if (body.indexOf('403 Forbidden') !== -1) {
+ self.display_error({
+ title: 'Access Denied',
+ error: 'Incorrect super-administrator password'
+ })
+ } else {
+ self.display_error({
+ title: 'Restore Database',
+ error: 'Could not restore the database'
+ })
+ }
+ },
+ complete: function() {
+ self.unblockUI();
+ self.do_notify(_t("Restored"), _t("Database restored successfully"));
}
});
},
- do_change_password: function() {
+ do_change_password: function(form) {
var self = this;
- self.$option_id.html(QWeb.render("Change_DB_Pwd", self));
-
- self.$option_id.find("form[name=change_pwd_form]").validate({
- messages: {
- old_pwd: "Please enter your previous password",
- new_pwd: "Please enter your new password",
- confirm_pwd: {
- required: "Please confirm your new password",
- equalTo: "The confirmation does not match the password"
- }
- },
- submitHandler: function (form) {
- self.rpc("/web/database/change_password", {
- 'fields': $(form).serializeArray()
- }, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- }
- self.do_notify("Changed Password", "Password has been changed successfully");
- });
+ self.rpc("/web/database/change_password", {
+ 'fields': $(form).serializeArray()
+ }, function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
}
+ self.do_notify("Changed Password", "Password has been changed successfully");
});
+ },
+ do_exit: function () {
}
});
-openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# */{
- remember_credentials: true,
-
+instance.web.Login = instance.web.Widget.extend({
template: "Login",
- /**
- * @constructs openerp.web.Login
- * @extends openerp.web.OldWidget
- *
- * @param parent
- * @param element_id
- */
-
+ remember_credentials: true,
init: function(parent) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
@@ -550,29 +458,35 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
},
start: function() {
var self = this;
- this.database = new openerp.web.Database(this);
- this.database.appendTo(this.$element);
- this.$element.find('#oe-db-config').click(function() {
- self.database.show();
- });
+ self.$element.find("form").submit(self.on_submit);
- this.$element.find("form").submit(this.on_submit);
-
- this.rpc("/web/database/get_list", {}, function(result) {
+ self.$element.find('.oe_login_manage_db').click(function() {
+ self.$element.find('.oe_login_bottom').hide();
+ self.$element.find('.oe_login_pane').hide();
+ self.databasemanager = new instance.web.DatabaseManager(self);
+ self.databasemanager.appendTo(self.$element);
+ self.databasemanager.do_exit.add_last(function() {
+ self.databasemanager.destroy();
+ self.$element.find('.oe_login_bottom').show();
+ self.$element.find('.oe_login_pane').show();
+ self.load_db_list();
+ })
+ });
+ self.load_db_list();
+ },
+ load_db_list: function () {
+ var self = this;
+ self.rpc("/web/database/get_list", {}, function(result) {
self.set_db_list(result.db_list);
- },
- function(error, event) {
+ }, function(error, event) {
if (error.data.fault_code === 'AccessDenied') {
event.preventDefault();
}
});
-
},
set_db_list: function (list) {
- this.$element.find("[name=db]").replaceWith(
- openerp.web.qweb.render('Login_dblist', {
- db_list: list, selected_db: this.selected_db}))
+ this.$element.find("[name=db]").replaceWith(instance.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db}))
},
on_submit: function(ev) {
if(ev) {
@@ -598,15 +512,15 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
*/
do_login: function (db, login, password) {
var self = this;
- this.$element.removeClass('login_invalid');
+ this.$element.removeClass('oe_login_invalid');
this.session.on_session_invalid.add({
callback: function () {
- self.$element.addClass("login_invalid");
+ self.$element.addClass("oe_login_invalid");
},
unique: true
});
this.session.session_authenticate(db, login, password).then(function() {
- self.$element.removeClass("login_invalid");
+ self.$element.removeClass("oe_login_invalid");
if (self.has_local_storage) {
if(self.remember_credentials) {
localStorage.setItem('last_db_login_success', db);
@@ -624,13 +538,7 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
}
});
-openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
- /**
- * @constructs openerp.web.Menu
- * @extends openerp.web.Widget
- *
- * @param parent
- */
+instance.web.Menu = instance.web.Widget.extend({
template: 'Menu',
init: function() {
this._super.apply(this, arguments);
@@ -774,14 +682,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
}
});
-openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMenu# */{
+instance.web.UserMenu = instance.web.Widget.extend({
template: "UserMenu",
- /**
- * @constructs openerp.web.UserMenu
- * @extends openerp.web.Widget
- *
- * @param parent
- */
init: function(parent) {
this._super(parent);
this.update_promise = $.Deferred().resolve();
@@ -805,7 +707,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
},
change_password :function() {
var self = this;
- this.dialog = new openerp.web.Dialog(this, {
+ this.dialog = new instance.web.Dialog(this, {
title: _t("Change Password"),
width : 'auto'
}).open();
@@ -819,14 +721,14 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
self.display_error(result);
return;
} else {
- openerp.webclient.on_logout();
+ instance.webclient.on_logout();
}
});
}
});
},
display_error: function (error) {
- return openerp.web.dialog($('<div>'), {
+ return instance.web.dialog($('<div>'), {
modal: true,
title: error.title,
buttons: [
@@ -841,10 +743,10 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
$avatar.attr('src', $avatar.data('default-src'));
if (!self.session.uid)
return;
- var func = new openerp.web.Model("res.users").get_func("read");
+ var func = new instance.web.Model("res.users").get_func("read");
return func(self.session.uid, ["name", "company_id"]).pipe(function(res) {
// TODO: Show company if multicompany is in use
- var topbar_name = _.str.sprintf("%s (%s)", res.name, openerp.connection.db, res.company_id[1]);
+ var topbar_name = _.str.sprintf("%s (%s)", res.name, instance.connection.db, res.company_id[1]);
self.$element.find('.oe_topbar_name').text(topbar_name);
var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=avatar&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
$avatar.attr('src', avatar_src);
@@ -858,7 +760,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
shortcut_load :function(){
var self = this,
sc = self.session.shortcuts,
- shortcuts_ds = new openerp.web.DataSet(this, 'ir.ui.view_sc');
+ shortcuts_ds = new instance.web.DataSet(this, 'ir.ui.view_sc');
self.$element.find('.oe_dropdown_options a[data-menu=shortcut]').each(function() {
$(this).parent().remove();
});
@@ -905,11 +807,12 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
},
on_menu_settings: function() {
var self = this;
- var action_manager = new openerp.web.ActionManager(this);
- var dataset = new openerp.web.DataSet (this,'res.users',this.context);
+ var action_manager = new instance.web.ActionManager(this);
+ var dataset = new instance.web.DataSet (this,'res.users',this.context);
dataset.call ('action_get','',function (result){
self.rpc('/web/action/load', {action_id:result}, function(result){
action_manager.do_action(_.extend(result['result'], {
+ target: 'inline',
res_id: self.session.uid,
res_model: 'res.users',
flags: {
@@ -922,7 +825,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
}));
});
});
- this.dialog = new openerp.web.Dialog(this,{
+ this.dialog = new instance.web.Dialog(this,{
title: _t("Preferences"),
width: '700px',
buttons: [
@@ -941,7 +844,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
]
}).open();
action_manager.appendTo(this.dialog.$element);
- action_manager.render(this.dialog);
+ action_manager.renderElement(this.dialog);
},
on_menu_about: function() {
var self = this;
@@ -952,7 +855,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
window.location = $.param.querystring(
window.location.href, 'debug');
});
- openerp.web.dialog($help, {autoOpen: true,
+ instance.web.dialog($help, {autoOpen: true,
modal: true, width: 960, title: _t("About")});
});
},
@@ -968,21 +871,17 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
}
});
-openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{
- /**
- * @constructs openerp.web.WebClient
- * @extends openerp.web.Widget
- */
+instance.web.WebClient = instance.web.Widget.extend({
init: function(parent) {
var self = this;
this._super(parent);
- openerp.webclient = this;
+ instance.webclient = this;
this.querystring = '?' + jQuery.param.querystring();
this._current_state = null;
},
start: function() {
var self = this;
- this.$element.addClass("openerp openerp2");
+ this.$element.addClass("openerp openerp-web-client-container");
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.$element.addClass("kitten-mode-activated");
this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
@@ -1001,7 +900,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
self.menu.do_reload();
if(self.action_manager)
self.action_manager.destroy();
- self.action_manager = new openerp.web.ActionManager(self);
+ self.action_manager = new instance.web.ActionManager(self);
self.action_manager.appendTo(self.$element.find('.oe_application'));
self.bind_hashchange();
var version_label = _t("OpenERP - Unsupported/Community Version");
@@ -1018,7 +917,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
var self = this;
this.destroy_content();
this.show_common();
- self.login = new openerp.web.Login(self);
+ self.login = new instance.web.Login(self);
self.login.appendTo(self.$element);
},
show_application: function() {
@@ -1027,10 +926,10 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
this.show_common();
self.$table = $(QWeb.render("WebClient", {}));
self.$element.append(self.$table);
- self.menu = new openerp.web.Menu(self);
+ self.menu = new instance.web.Menu(self);
self.menu.replace(this.$element.find('.oe_menu_placeholder'));
self.menu.on_action.add(this.proxy('on_menu_action'));
- self.user_menu = new openerp.web.UserMenu(self);
+ self.user_menu = new instance.web.UserMenu(self);
self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder'));
self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
self.user_menu.on_action.add(this.proxy('on_menu_action'));
@@ -1038,8 +937,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
show_common: function() {
var self = this;
if (!this.crashmanager) {
- this.crashmanager = new openerp.web.CrashManager();
- openerp.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
+ this.crashmanager = new instance.web.CrashManager();
+ instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
window.onerror = function (message, file, line) {
self.crashmanager.on_traceback({
type: _t("Client Error"),
@@ -1048,9 +947,9 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
});
}
}
- this.notification = new openerp.web.Notification(this);
+ this.notification = new instance.web.Notification(this);
this.notification.appendTo(this.$element);
- this.loading = new openerp.web.Loading(this);
+ this.loading = new instance.web.Loading(this);
this.loading.appendTo(this.$element);
},
destroy_content: function() {
@@ -1062,7 +961,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
do_reload: function() {
var self = this;
return this.session.session_reload().pipe(function () {
- openerp.connection.load_modules(true).pipe(
+ instance.connection.load_modules(true).pipe(
self.menu.proxy('do_reload')); });
},
@@ -1126,16 +1025,15 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
}
});
-openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({
+instance.web.EmbeddedClient = instance.web.Widget.extend({
template: 'EmptyComponent',
- init: function(action_id, options) {
- this._super();
+ init: function(parent, action_id, options) {
+ this._super(parent);
// TODO take the xmlid of a action instead of its id
this.action_id = action_id;
this.options = options || {};
- this.am = new openerp.web.ActionManager(this);
+ this.am = new instance.web.ActionManager(this);
},
-
start: function() {
var self = this;
this.am.appendTo(this.$element.addClass('openerp'));
@@ -1152,10 +1050,9 @@ openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({
self.am.do_action(action);
});
}
-
});
-openerp.web.embed = function (origin, dbname, login, key, action, options) {
+instance.web.embed = function (origin, dbname, login, key, action, options) {
$('head').append($('<link>', {
'rel': 'stylesheet',
'type': 'text/css',
@@ -1166,9 +1063,9 @@ openerp.web.embed = function (origin, dbname, login, key, action, options) {
var sc = document.getElementsByTagName('script');
currentScript = sc[sc.length-1];
}
- openerp.connection.session_bind(origin).then(function () {
- openerp.connection.session_authenticate(dbname, login, key, true).then(function () {
- var client = new openerp.web.EmbeddedClient(action, options);
+ instance.connection.session_bind(origin).then(function () {
+ instance.connection.session_authenticate(dbname, login, key, true).then(function () {
+ var client = new instance.web.EmbeddedClient(null, action, options);
client.insertAfter(currentScript);
});
});
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/corelib.js
index 44e15f6dd13..cbd28aca8a3 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/corelib.js
@@ -1,60 +1,134 @@
-/*---------------------------------------------------------
- * OpenERP Web core
- *--------------------------------------------------------*/
-var console;
-if (!console) {
- console = {log: function () {}};
-}
-if (!console.debug) {
- console.debug = console.log;
-}
+/*
+ * Copyright (c) 2012, OpenERP S.A.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
-openerp.web.core = function(openerp) {
+openerp.web.corelib = function(instance) {
-// a function to override the "extend()" method of JR's inheritance, allowing
-// the usage of "include()"
-oe_override_class = function(claz){
- var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ?
- /\b_super\b/ : /.*/;
-
- // Create a new Class that inherits from this class
- claz.extend = function(prop) {
+/**
+ * Improved John Resig's inheritance, based on:
+ *
+ * Simple JavaScript Inheritance
+ * By John Resig http://ejohn.org/
+ * MIT Licensed.
+ *
+ * Adds "include()"
+ *
+ * Defines The Class object. That object can be used to define and inherit classes using
+ * the extend() method.
+ *
+ * Example:
+ *
+ * var Person = instance.web.Class.extend({
+ * init: function(isDancing){
+ * this.dancing = isDancing;
+ * },
+ * dance: function(){
+ * return this.dancing;
+ * }
+ * });
+ *
+ * The init() method act as a constructor. This class can be instancied this way:
+ *
+ * var person = new Person(true);
+ * person.dance();
+ *
+ * The Person class can also be extended again:
+ *
+ * var Ninja = Person.extend({
+ * init: function(){
+ * this._super( false );
+ * },
+ * dance: function(){
+ * // Call the inherited version of dance()
+ * return this._super();
+ * },
+ * swingSword: function(){
+ * return true;
+ * }
+ * });
+ *
+ * When extending a class, each re-defined method can use this._super() to call the previous
+ * implementation of that method.
+ */
+(function() {
+ var initializing = false,
+ fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+ // The web Class implementation (does nothing)
+ instance.web.Class = function(){};
+
+ /**
+ * Subclass an existing class
+ *
+ * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
+ */
+ instance.web.Class.extend = function() {
var _super = this.prototype;
-
- // Instantiate a base class (but only create the instance, don't run the
- // init constructor)
- initializing = true; var prototype = new this(); initializing = false;
-
+ // Support mixins arguments
+ var args = _.toArray(arguments);
+ args.unshift({});
+ var prop = _.extend.apply(_,args);
+
+ // Instantiate a web class (but only create the instance,
+ // don't run the init constructor)
+ initializing = true;
+ var prototype = new this();
+ initializing = false;
+
// Copy the properties over onto the new prototype
for (var name in prop) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn){
- return function() {
- var tmp = this._super;
-
- // Add a new ._super() method that is the same method but on the
- // super-class
- this._super = _super[name];
-
- // The method only need to be bound temporarily, so we remove it
- // when we're done executing
- var ret = fn.apply(this, arguments); this._super = tmp;
-
- return ret;
- };
- })(name, prop[name]) : prop[name];
+ // Check if we're overwriting an existing function
+ prototype[name] = typeof prop[name] == "function" &&
+ typeof _super[name] == "function" &&
+ fnTest.test(prop[name]) ?
+ (function(name, fn) {
+ return function() {
+ var tmp = this._super;
+
+ // Add a new ._super() method that is the same
+ // method but on the super-class
+ this._super = _super[name];
+
+ // The method only need to be bound temporarily, so
+ // we remove it when we're done executing
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+
+ return ret;
+ };
+ })(name, prop[name]) :
+ prop[name];
}
-
+
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init) {
- var ret = this.init.apply(this, arguments); if (ret) { return ret;}
- } return this;
+ var ret = this.init.apply(this, arguments);
+ if (ret) { return ret; }
+ }
+ return this;
}
-
Class.include = function (properties) {
for (var name in properties) {
if (typeof properties[name] !== 'function'
@@ -64,138 +138,617 @@ oe_override_class = function(claz){
&& prototype.hasOwnProperty(name)) {
prototype[name] = (function (name, fn, previous) {
return function () {
- var tmp = this._super; this._super = previous; var
- ret = fn.apply(this, arguments); this._super = tmp;
+ var tmp = this._super;
+ this._super = previous;
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
return ret;
}
})(name, properties[name], prototype[name]);
} else if (typeof _super[name] === 'function') {
prototype[name] = (function (name, fn) {
return function () {
- var tmp = this._super; this._super = _super[name];
- var ret = fn.apply(this, arguments); this._super =
- tmp; return ret;
+ var tmp = this._super;
+ this._super = _super[name];
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+ return ret;
}
})(name, properties[name]);
}
}
};
-
+
// Populate our constructed prototype object
Class.prototype = prototype;
-
+
// Enforce the constructor to be what we expect
- Class.prototype.constructor = Class;
-
+ Class.constructor = Class;
+
// And make this class extendable
Class.extend = arguments.callee;
-
+
return Class;
};
-};
-oe_override_class(nova.Class);
-oe_override_class(nova.Widget);
+})();
-openerp.web.Class = nova.Class;
+// Mixins
-openerp.web.callback = function(obj, method) {
- var callback = function() {
- var args = Array.prototype.slice.call(arguments);
- var r;
- for(var i = 0; i < callback.callback_chain.length; i++) {
- var c = callback.callback_chain[i];
- if(c.unique) {
- callback.callback_chain.splice(i, 1);
- i -= 1;
- }
- var result = c.callback.apply(c.self, c.args.concat(args));
- if (c.callback === method) {
- // return the result of the original method
- r = result;
+/**
+ * Mixin to structure objects' life-cycles folowing a parent-children
+ * relationship. Each object can a have a parent and multiple children.
+ * When an object is destroyed, all its children are destroyed too releasing
+ * any resource they could have reserved before.
+ */
+instance.web.ParentedMixin = {
+ __parentedMixin : true,
+ init: function() {
+ this.__parentedDestroyed = false;
+ this.__parentedChildren = [];
+ this.__parentedParent = null;
+ },
+ /**
+ * Set the parent of the current object. When calling this method, the
+ * parent will also be informed and will return the current object
+ * when its getChildren() method is called. If the current object did
+ * already have a parent, it is unregistered before, which means the
+ * previous parent will not return the current object anymore when its
+ * getChildren() method is called.
+ */
+ setParent : function(parent) {
+ if (this.getParent()) {
+ if (this.getParent().__parentedMixin) {
+ this.getParent().__parentedChildren = _.without(this
+ .getParent().getChildren(), this);
}
- // TODO special value to stop the chain
- // openerp.web.callback_stop
- }
- return r;
- };
- callback.callback_chain = [];
- callback.add = function(f) {
- if(typeof(f) == 'function') {
- f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
}
- f.self = f.self || null;
- f.args = f.args || [];
- f.unique = !!f.unique;
- if(f.position == 'last') {
- callback.callback_chain.push(f);
- } else {
- callback.callback_chain.unshift(f);
+ this.__parentedParent = parent;
+ if (parent && parent.__parentedMixin) {
+ parent.__parentedChildren.push(this);
}
- return callback;
- };
- callback.add_first = function(f) {
- return callback.add.apply(null,arguments);
- };
- callback.add_last = function(f) {
- return callback.add({
- callback: f,
- args: Array.prototype.slice.call(arguments, 1),
- position: "last"
+ },
+ /**
+ * Return the current parent of the object (or null).
+ */
+ getParent : function() {
+ return this.__parentedParent;
+ },
+ /**
+ * Return a list of the children of the current object.
+ */
+ getChildren : function() {
+ return _.clone(this.__parentedChildren);
+ },
+ /**
+ * Returns true if destroy() was called on the current object.
+ */
+ isDestroyed : function() {
+ return this.__parentedDestroyed;
+ },
+ /**
+ * Inform the object it should destroy itself, releasing any
+ * resource it could have reserved.
+ */
+ destroy : function() {
+ _.each(this.getChildren(), function(el) {
+ el.destroy();
});
- };
- callback.remove = function(f) {
- callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) {
- return el.callback === f;
- }));
- return callback;
- };
-
- return callback.add({
- callback: method,
- self:obj,
- args:Array.prototype.slice.call(arguments, 2)
- });
+ this.setParent(undefined);
+ this.__parentedDestroyed = true;
+ }
};
/**
- * web error for lookup failure
+ * TODO al: move into the the mixin
+ *
+ * Backbone's events
+ *
+ * (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
+ * Backbone may be freely distributed under the MIT license.
+ * For all details and documentation:
+ * http://backbonejs.org
+ *
+ * This class just handle the dispatching of events, it is not meant to be extended,
+ * nor used directly. All integration with parenting and automatic unregistration of
+ * events is done in EventDispatcherMixin.
*
- * @class
*/
-openerp.web.NotFound = openerp.web.Class.extend( /** @lends openerp.web.NotFound# */ {
+instance.web.Events = instance.web.Class.extend({
+
+ on : function(events, callback, context) {
+ var ev;
+ events = events.split(/\s+/);
+ var calls = this._callbacks || (this._callbacks = {});
+ while (ev = events.shift()) {
+ var list = calls[ev] || (calls[ev] = {});
+ var tail = list.tail || (list.tail = list.next = {});
+ tail.callback = callback;
+ tail.context = context;
+ list.tail = tail.next = {};
+ }
+ return this;
+ },
+
+ off : function(events, callback, context) {
+ var ev, calls, node;
+ if (!events) {
+ delete this._callbacks;
+ } else if (calls = this._callbacks) {
+ events = events.split(/\s+/);
+ while (ev = events.shift()) {
+ node = calls[ev];
+ delete calls[ev];
+ if (!callback || !node)
+ continue;
+ while ((node = node.next) && node.next) {
+ if (node.callback === callback
+ && (!context || node.context === context))
+ continue;
+ this.on(ev, node.callback, node.context);
+ }
+ }
+ }
+ return this;
+ },
+
+ trigger : function(events) {
+ var event, node, calls, tail, args, all, rest;
+ if (!(calls = this._callbacks))
+ return this;
+ all = calls['all'];
+ (events = events.split(/\s+/)).push(null);
+ // Save references to the current heads & tails.
+ while (event = events.shift()) {
+ if (all)
+ events.push({
+ next : all.next,
+ tail : all.tail,
+ event : event
+ });
+ if (!(node = calls[event]))
+ continue;
+ events.push({
+ next : node.next,
+ tail : node.tail
+ });
+ }
+ rest = Array.prototype.slice.call(arguments, 1);
+ while (node = events.pop()) {
+ tail = node.tail;
+ args = node.event ? [ node.event ].concat(rest) : rest;
+ while ((node = node.next) !== tail) {
+ node.callback.apply(node.context || this, args);
+ }
+ }
+ return this;
+ }
+});
+
+instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, {
+ __eventDispatcherMixin: true,
+ init: function() {
+ instance.web.ParentedMixin.init.call(this);
+ this.__edispatcherEvents = new instance.web.Events();
+ this.__edispatcherRegisteredEvents = [];
+ },
+ on: function(events, dest, func) {
+ var self = this;
+ events = events.split(/\s+/);
+ _.each(events, function(eventName) {
+ self.__edispatcherEvents.on(eventName, func, dest);
+ if (dest && dest.__eventDispatcherMixin) {
+ dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
+ }
+ });
+ return this;
+ },
+ off: function(events, dest, func) {
+ var self = this;
+ events = events.split(/\s+/);
+ _.each(events, function(eventName) {
+ self.__edispatcherEvents.off(eventName, func, dest);
+ if (dest && dest.__eventDispatcherMixin) {
+ dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
+ return !(el.name === eventName && el.func === func && el.source === self);
+ });
+ }
+ });
+ return this;
+ },
+ trigger: function(events) {
+ this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
+ return this;
+ },
+ destroy: function() {
+ var self = this;
+ _.each(this.__edispatcherRegisteredEvents, function(event) {
+ event.source.__edispatcherEvents.off(event.name, event.func, self);
+ });
+ this.__edispatcherRegisteredEvents = [];
+ if(!this.__edispatcherEvents) {
+ debugger;
+ }
+ this.__edispatcherEvents.off();
+ instance.web.ParentedMixin.destroy.call(this);
+ }
+});
+
+instance.web.GetterSetterMixin = _.extend({}, instance.web.EventDispatcherMixin, {
+ init: function() {
+ instance.web.EventDispatcherMixin.init.call(this);
+ this.__getterSetterInternalMap = {};
+ },
+ set: function(map) {
+ var self = this;
+ var changed = false;
+ _.each(map, function(val, key) {
+ var tmp = self.__getterSetterInternalMap[key];
+ if (tmp === val)
+ return;
+ changed = true;
+ self.__getterSetterInternalMap[key] = val;
+ self.trigger("change:" + key, self, {
+ oldValue: tmp,
+ newValue: val
+ });
+ });
+ if (changed)
+ self.trigger("change", self);
+ },
+ get: function(key) {
+ return this.__getterSetterInternalMap[key];
+ }
+});
+
+instance.web.CallbackEnabledMixin = _.extend({}, instance.web.GetterSetterMixin, {
+ init: function() {
+ instance.web.GetterSetterMixin.init.call(this);
+ var self = this;
+ var callback_maker = function(obj, name, method) {
+ var callback = function() {
+ var args = Array.prototype.slice.call(arguments);
+ self.trigger.apply(self, [name].concat(args));
+ var r;
+ for(var i = 0; i < callback.callback_chain.length; i++) {
+ var c = callback.callback_chain[i];
+ if(c.unique) {
+ callback.callback_chain.splice(i, 1);
+ i -= 1;
+ }
+ var result = c.callback.apply(c.self, c.args.concat(args));
+ if (c.callback === method) {
+ // return the result of the original method
+ r = result;
+ }
+ // TODO special value to stop the chain
+ // instance.web.callback_stop
+ }
+ return r;
+ };
+ callback.callback_chain = [];
+ callback.add = function(f) {
+ if(typeof(f) == 'function') {
+ f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
+ }
+ f.self = f.self || null;
+ f.args = f.args || [];
+ f.unique = !!f.unique;
+ if(f.position == 'last') {
+ callback.callback_chain.push(f);
+ } else {
+ callback.callback_chain.unshift(f);
+ }
+ return callback;
+ };
+ callback.add_first = function(f) {
+ return callback.add.apply(null,arguments);
+ };
+ callback.add_last = function(f) {
+ return callback.add({
+ callback: f,
+ args: Array.prototype.slice.call(arguments, 1),
+ position: "last"
+ });
+ };
+ callback.remove = function(f) {
+ callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) {
+ return el.callback === f;
+ }));
+ return callback;
+ };
+
+ return callback.add({
+ callback: method,
+ self:obj,
+ args:Array.prototype.slice.call(arguments, 3)
+ });
+ };
+ // Transform on_/do_* methods into callbacks
+ for (var name in this) {
+ if(typeof(this[name]) == "function") {
+ this[name].debug_name = name;
+ if((/^on_|^do_/).test(name)) {
+ this[name] = callback_maker(this, name, this[name]);
+ }
+ }
+ }
+ },
+ /**
+ * Proxies a method of the object, in order to keep the right ``this`` on
+ * method invocations.
+ *
+ * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
+ * even more so to ``jQuery.proxy`` with a fundamental difference: its
+ * resolution of the method being called is lazy, meaning it will use the
+ * method as it is when the proxy is called, not when the proxy is created.
+ *
+ * Other methods will fix the bound method to what it is when creating the
+ * binding/proxy, which is fine in most javascript code but problematic in
+ * OpenERP Web where developers may want to replace existing callbacks with
+ * theirs.
+ *
+ * The semantics of this precisely replace closing over the method call.
+ *
+ * @param {String} method_name name of the method to invoke
+ * @returns {Function} proxied method
+ */
+ proxy: function (method_name) {
+ var self = this;
+ return function () {
+ return self[method_name].apply(self, arguments);
+ }
+ }
});
-openerp.web.KeyNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.KeyNotFound# */ {
+
+instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
+ /**
+ * Tag name when creating a default $element.
+ * @type string
+ */
+ tagName: 'div',
/**
- * Thrown when a key could not be found in a mapping
+ * Constructs the widget and sets its parent if a parent is given.
+ *
+ * @constructs instance.web.Widget
+ * @extends instance.web.CallbackEnabled
*
- * @constructs openerp.web.KeyNotFound
- * @extends openerp.web.NotFound
- * @param {String} key the key which could not be found
+ * @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
+ * When that widget is destroyed by calling destroy(), the current instance will be
+ * destroyed too. Can be null.
+ * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
+ * to bind the current Widget to an already existing part of the DOM, which is not compatible
+ * with the DOM insertion methods provided by the current implementation of Widget. So
+ * for new components this argument should not be provided any more.
*/
- init: function (key) {
- this.key = key;
+ init: function(parent) {
+ instance.web.CallbackEnabledMixin.init.call(this);
+ this.$element = $(document.createElement(this.tagName));
+ this.setParent(parent);
+ },
+ /**
+ * Destroys the current widget, also destroys all its children before destroying itself.
+ */
+ destroy: function() {
+ _.each(this.getChildren(), function(el) {
+ el.destroy();
+ });
+ if(this.$element != null) {
+ this.$element.remove();
+ }
+ instance.web.GetterSetterMixin.destroy.call(this);
+ },
+ /**
+ * Renders the current widget and appends it to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ appendTo: function(target) {
+ var self = this;
+ return this.__widgetRenderAndInsert(function(t) {
+ self.$element.appendTo(t);
+ }, target);
+ },
+ /**
+ * Renders the current widget and prepends it to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ prependTo: function(target) {
+ var self = this;
+ return this.__widgetRenderAndInsert(function(t) {
+ self.$element.prependTo(t);
+ }, target);
+ },
+ /**
+ * Renders the current widget and inserts it after to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ insertAfter: function(target) {
+ var self = this;
+ return this.__widgetRenderAndInsert(function(t) {
+ self.$element.insertAfter(t);
+ }, target);
+ },
+ /**
+ * Renders the current widget and inserts it before to the given jQuery object or Widget.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ insertBefore: function(target) {
+ var self = this;
+ return this.__widgetRenderAndInsert(function(t) {
+ self.$element.insertBefore(t);
+ }, target);
+ },
+ /**
+ * Renders the current widget and replaces the given jQuery object.
+ *
+ * @param target A jQuery object or a Widget instance.
+ */
+ replace: function(target) {
+ return this.__widgetRenderAndInsert(_.bind(function(t) {
+ this.$element.replaceAll(t);
+ }, this), target);
},
- toString: function () {
- return "The key " + this.key + " was not found";
+ __widgetRenderAndInsert: function(insertion, target) {
+ this.renderElement();
+ insertion(target);
+ return this.start();
+ },
+ /**
+ * This is the method to implement to render the Widget.
+ */
+ renderElement: function() {
+ },
+ /**
+ * Method called after rendering. Mostly used to bind actions, perform asynchronous
+ * calls, etc...
+ *
+ * By convention, the method should return a promise to inform the caller when
+ * this widget has been initialized.
+ *
+ * @returns {jQuery.Deferred}
+ */
+ start: function() {
+ }
+});
+
+// Classes
+
+instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEnabledMixin, {
+ init: function() {
+ instance.web.CallbackEnabledMixin.init.call(this);
}
});
-openerp.web.ObjectNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.ObjectNotFound# */ {
+
+/**
+ * Base class for all visual components. Provides a lot of functionalities helpful
+ * for the management of a part of the DOM.
+ *
+ * Widget handles:
+ * - Rendering with QWeb.
+ * - Life-cycle management and parenting (when a parent is destroyed, all its children are
+ * destroyed too).
+ * - Insertion in DOM.
+ *
+ * Guide to create implementations of the Widget class:
+ * ==============================================
+ *
+ * Here is a sample child class:
+ *
+ * MyWidget = instance.base.Widget.extend({
+ * // the name of the QWeb template to use for rendering
+ * template: "MyQWebTemplate",
+ *
+ * init: function(parent) {
+ * this._super(parent);
+ * // stuff that you want to init before the rendering
+ * },
+ * start: function() {
+ * // stuff you want to make after the rendering, `this.$element` holds a correct value
+ * this.$element.find(".my_button").click(/* an example of event binding * /);
+ *
+ * // if you have some asynchronous operations, it's a good idea to return
+ * // a promise in start()
+ * var promise = this.rpc(...);
+ * return promise;
+ * }
+ * });
+ *
+ * Now this class can simply be used with the following syntax:
+ *
+ * var my_widget = new MyWidget(this);
+ * my_widget.appendTo($(".some-div"));
+ *
+ * With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
+ * DOM inside the ".some-div" div and its events were binded.
+ *
+ * And of course, when you don't need that widget anymore, just do:
+ *
+ * my_widget.destroy();
+ *
+ * That will kill the widget in a clean way and erase its content from the dom.
+ */
+instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
/**
- * Thrown when an object path does not designate a valid class or object
- * in the openerp hierarchy.
+ * The name of the QWeb template that will be used for rendering. Must be
+ * redefined in subclasses or the default render() method can not be used.
+ *
+ * @type string
+ */
+ template: null,
+ /**
+ * Constructs the widget and sets its parent if a parent is given.
*
- * @constructs openerp.web.ObjectNotFound
- * @extends openerp.web.NotFound
- * @param {String} path the invalid object path
+ * @constructs instance.web.Widget
+ * @extends instance.web.CallbackEnabled
+ *
+ * @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
+ * When that widget is destroyed by calling destroy(), the current instance will be
+ * destroyed too. Can be null.
+ * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
+ * to bind the current Widget to an already existing part of the DOM, which is not compatible
+ * with the DOM insertion methods provided by the current implementation of Widget. So
+ * for new components this argument should not be provided any more.
+ */
+ init: function(parent) {
+ instance.web.WidgetMixin.init.call(this,parent);
+ this.session = instance.connection;
+ },
+ /**
+ * Renders the element. The default implementation renders the widget using QWeb,
+ * `this.template` must be defined. The context given to QWeb contains the "widget"
+ * key that references `this`.
*/
- init: function (path) {
- this.path = path;
+ renderElement: function() {
+ var rendered = null;
+ if (this.template)
+ rendered = instance.web.qweb.render(this.template, {widget: this});
+ if (_.str.trim(rendered)) {
+ var elem = $(rendered);
+ this.$element.replaceWith(elem);
+ this.$element = elem;
+ }
},
- toString: function () {
- return "Could not find any object of path " + this.path;
+ /**
+ * Informs the action manager to do an action. This supposes that
+ * the action manager can be found amongst the ancestors of the current widget.
+ * If that's not the case this method will simply return `false`.
+ */
+ do_action: function(action, on_finished) {
+ if (this.getParent()) {
+ return this.getParent().do_action(action, on_finished);
+ }
+ return false;
+ },
+ do_notify: function() {
+ if (this.getParent()) {
+ return this.getParent().do_notify.apply(this,arguments);
+ }
+ return false;
+ },
+ do_warn: function() {
+ if (this.getParent()) {
+ return this.getParent().do_warn.apply(this,arguments);
+ }
+ return false;
+ },
+ rpc: function(url, data, success, error) {
+ var def = $.Deferred().then(success, error);
+ var self = this;
+ instance.connection.rpc(url, data). then(function() {
+ if (!self.isDestroyed())
+ def.resolve.apply(def, arguments);
+ }, function() {
+ if (!self.isDestroyed())
+ def.reject.apply(def, arguments);
+ });
+ return def.promise();
}
});
-openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry# */ {
+
+instance.web.Registry = instance.web.Class.extend({
/**
* Stores a mapping of arbitrary key (strings) to object paths (as strings
* as well).
@@ -204,11 +757,11 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
* object, even if those objects have been overloaded/replaced after the
* registry was created.
*
- * An object path is simply a dotted name from the openerp root to the
- * object pointed to (e.g. ``"openerp.web.Connection"`` for an OpenERP
+ * An object path is simply a dotted name from the instance root to the
+ * object pointed to (e.g. ``"instance.web.Connection"`` for an OpenERP
* connection object).
*
- * @constructs openerp.web.Registry
+ * @constructs instance.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
@@ -220,10 +773,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
*
* @param {String} key the key to fetch the object for
* @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
- * @returns {Class} the stored class, to initialize
- *
- * @throws {openerp.web.KeyNotFound} if the object was not in the mapping
- * @throws {openerp.web.ObjectNotFound} if the object path was invalid
+ * @returns {Class} the stored class, to initialize or null if not found
*/
get_object: function (key, silent_error) {
var path_string = this.map[key];
@@ -232,10 +782,10 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
return this.parent.get_object(key, silent_error);
}
if (silent_error) { return void 'nooo'; }
- throw new openerp.web.KeyNotFound(key);
+ return null;
}
- var object_match = openerp;
+ var object_match = instance;
var path = path_string.split('.');
// ignore first section
for(var i=1; i<path.length; ++i) {
@@ -243,7 +793,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
if (object_match === undefined) {
if (silent_error) { return void 'noooooo'; }
- throw new openerp.web.ObjectNotFound(path_string);
+ return null;
}
}
return object_match;
@@ -269,9 +819,6 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
*
* @param {Array} keys a sequence of keys to fetch the object for
* @returns {Class} the first class found matching an object
- *
- * @throws {openerp.web.KeyNotFound} if none of the keys was in the mapping
- * @trows {openerp.web.ObjectNotFound} if a found object path was invalid
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
@@ -282,7 +829,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
return this.get_object(key);
}
- throw new openerp.web.KeyNotFound(keys.join(','));
+ return null;
},
/**
* Adds a new key and value to the registry.
@@ -291,7 +838,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
*
* @param {String} key
* @param {String} object_path fully qualified dotted object path
- * @returns {openerp.web.Registry} itself
+ * @returns {instance.web.Registry} itself
*/
add: function (key, object_path) {
this.map[key] = object_path;
@@ -307,7 +854,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
extend: function (mapping) {
- var child = new openerp.web.Registry(mapping);
+ var child = new instance.web.Registry(mapping);
child.parent = this;
return child;
},
@@ -320,57 +867,10 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
}
});
-openerp.web.CallbackEnabledMixin = {
- init: function() {
- // Transform on_* method into openerp.web.callbacks
- for (var name in this) {
- if(typeof(this[name]) == "function") {
- this[name].debug_name = name;
- // bind ALL function to this not only on_and _do ?
- if((/^on_|^do_/).test(name)) {
- this[name] = openerp.web.callback(this, this[name]);
- }
- }
- }
- },
- /**
- * Proxies a method of the object, in order to keep the right ``this`` on
- * method invocations.
- *
- * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
- * even more so to ``jQuery.proxy`` with a fundamental difference: its
- * resolution of the method being called is lazy, meaning it will use the
- * method as it is when the proxy is called, not when the proxy is created.
- *
- * Other methods will fix the bound method to what it is when creating the
- * binding/proxy, which is fine in most javascript code but problematic in
- * OpenERP Web where developers may want to replace existing callbacks with
- * theirs.
- *
- * The semantics of this precisely replace closing over the method call.
- *
- * @param {String} method_name name of the method to invoke
- * @returns {Function} proxied method
- */
- proxy: function (method_name) {
- var self = this;
- return function () {
- return self[method_name].apply(self, arguments);
- }
- }
-};
-
-openerp.web.CallbackEnabled = openerp.web.Class.extend(_.extend({}, nova.GetterSetterMixin, openerp.web.CallbackEnabledMixin, {
- init: function() {
- nova.GetterSetterMixin.init.call(this);
- openerp.web.CallbackEnabledMixin.init.call(this);
- }
-}));
-
-openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Connection# */{
+instance.web.Connection = instance.web.CallbackEnabled.extend( /** @lends instance.web.Connection# */{
/**
- * @constructs openerp.web.Connection
- * @extends openerp.web.CallbackEnabled
+ * @constructs instance.web.Connection
+ * @extends instance.web.CallbackEnabled
*
* @param {String} [server] JSON-RPC endpoint hostname
* @param {String} [port] JSON-RPC endpoint port
@@ -380,7 +880,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.server = null;
this.debug = ($.deparam($.param.querystring()).debug != undefined);
// TODO: session store in cookie should be optional
- this.name = openerp._session_id;
+ this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
session_bind: function(origin) {
@@ -388,7 +888,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
this.prefix = this.origin;
this.server = this.origin; // keep chs happy
- openerp.web.qweb.default_dict['_s'] = this.origin;
+ instance.web.qweb.default_dict['_s'] = this.origin;
this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp;
this.session_id = false;
this.uid = false;
@@ -396,7 +896,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.user_context= {};
this.db = false;
this.openerp_entreprise = false;
- this.module_list = openerp._modules.slice();
+ this.module_list = instance._modules.slice();
this.module_loaded = {};
_(this.module_list).each(function (mod) {
self.module_loaded[mod] = true;
@@ -569,7 +1069,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
try {
var ctx = this.test_eval_contexts(source.contexts);
if (!_.isEqual(ctx, expected.context)) {
- openerp.webclient.notification.warn('Context mismatch, report to xmo',
+ instance.webclient.notification.warn('Context mismatch, report to xmo',
_.str.sprintf(match_template, {
source: JSON.stringify(source.contexts),
local: JSON.stringify(ctx),
@@ -577,17 +1077,17 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
}), true);
}
} catch (e) {
- openerp.webclient.notification.warn('Context fail, report to xmo',
+ instance.webclient.notification.warn('Context fail, report to xmo',
_.str.sprintf(fail_template, {
error: e.message,
- source: source.contexts
+ source: JSON.stringify(source.contexts)
}), true);
}
try {
var dom = this.test_eval_domains(source.domains, this.test_eval_get_context());
if (!_.isEqual(dom, expected.domain)) {
- openerp.webclient.notification.warn('Domains mismatch, report to xmo',
+ instance.webclient.notification.warn('Domains mismatch, report to xmo',
_.str.sprintf(match_template, {
source: JSON.stringify(source.domains),
local: JSON.stringify(dom),
@@ -595,17 +1095,17 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
}), true);
}
} catch (e) {
- openerp.webclient.notification.warn('Domain fail, report to xmo',
+ instance.webclient.notification.warn('Domain fail, report to xmo',
_.str.sprintf(fail_template, {
error: e.message,
- source: source.domains
+ source: JSON.stringify(source.domains)
}), true);
}
try {
var groups = this.test_eval_groupby(source.group_by_seq);
if (!_.isEqual(groups, expected.group_by)) {
- openerp.webclient.notification.warn('GroupBy mismatch, report to xmo',
+ instance.webclient.notification.warn('GroupBy mismatch, report to xmo',
_.str.sprintf(match_template, {
source: JSON.stringify(source.group_by_seq),
local: JSON.stringify(groups),
@@ -613,43 +1113,51 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
}), true);
}
} catch (e) {
- openerp.webclient.notification.warn('GroupBy fail, report to xmo',
+ instance.webclient.notification.warn('GroupBy fail, report to xmo',
_.str.sprintf(fail_template, {
error: e.message,
- source: source.group_by_seq
+ source: JSON.stringify(source.group_by_seq)
}), true);
}
},
- test_eval_contexts: function (contexts) {
- var result_context = _.extend({}, this.user_context),
- self = this;
- _(contexts).each(function (ctx) {
+ test_eval_contexts: function (contexts, evaluation_context) {
+ evaluation_context = evaluation_context || {};
+ var self = this;
+ return _(contexts).reduce(function (result_context, ctx) {
+ // __eval_context evaluations can lead to some of `contexts`'s
+ // values being null, skip them as well as empty contexts
+ if (_.isEmpty(ctx)) { return result_context; }
+ var evaluated = ctx;
switch(ctx.__ref) {
case 'context':
- _.extend(result_context, py.eval(ctx.__debug));
+ evaluated = py.eval(ctx.__debug, evaluation_context);
break;
case 'compound_context':
- _.extend(
- result_context, self.test_eval_contexts(ctx.__contexts));
+ var eval_context = self.test_eval_contexts([ctx.__eval_context]);
+ evaluated = self.test_eval_contexts(
+ ctx.__contexts, _.extend({}, evaluation_context, eval_context));
break;
- default:
- _.extend(result_context, ctx);
}
- });
- return result_context;
+ // add newly evaluated context to evaluation context for following
+ // siblings
+ _.extend(evaluation_context, evaluated);
+ return _.extend(result_context, evaluated);
+ }, _.extend({}, this.user_context));
},
- test_eval_domains: function (domains, eval_context) {
+ test_eval_domains: function (domains, evaluation_context) {
var result_domain = [], self = this;
_(domains).each(function (dom) {
switch(dom.__ref) {
case 'domain':
result_domain.push.apply(
- result_domain, py.eval(dom.__debug, eval_context));
+ result_domain, py.eval(dom.__debug, evaluation_context));
break;
case 'compound_domain':
+ var eval_context = self.test_eval_contexts([dom.__eval_context]);
result_domain.push.apply(
result_domain, self.test_eval_domains(
- dom.__domains, eval_context));
+ dom.__domains, _.extend(
+ {}, evaluation_context, eval_context)));
break;
default:
result_domain.push.apply(
@@ -667,7 +1175,8 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
group = py.eval(ctx.__debug).group_by;
break;
case 'compound_context':
- group = self.test_eval_contexts(ctx.__contexts).group_by;
+ group = self.test_eval_contexts(
+ ctx.__contexts, ctx.__eval_context).group_by;
break;
default:
group = ctx.group_by
@@ -691,7 +1200,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
* setting the correct session id and session context in the parameter
* objects
*
- * @param {Object} url RPC endpoint
+ * @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
@@ -715,7 +1224,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
};
var deferred = $.Deferred();
this.on_rpc_request();
- this.rpc_function(url, payload).then(
+ var aborter = params.aborter;
+ delete params.aborter;
+ var request = this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
self.on_rpc_response();
if (!response.error) {
@@ -744,6 +1255,16 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
};
deferred.reject(error, $.Event());
});
+ if (aborter) {
+ aborter.abort_last = function () {
+ if (!(request.isResolved() || request.isRejected())) {
+ deferred.fail(function (error, event) {
+ event.preventDefault();
+ });
+ request.abort();
+ }
+ };
+ }
// Allow deferred user to disable on_rpc_error in fail
deferred.fail(function() {
deferred.fail(function(error, event) {
@@ -848,7 +1369,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
// TODO: session store in cookie should be optional
this.session_id = this.get_cookie('session_id');
return this.session_reload().pipe(function(result) {
- var modules = openerp._modules.join(',');
+ var modules = instance._modules.join(',');
var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb);
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
@@ -971,7 +1492,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
self.rpc('/web/webclient/translations', params).pipe(function(trans) {
- openerp.web._t.database.set_bundle(trans);
+ instance.web._t.database.set_bundle(trans);
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
return self.do_load_js(file_list.concat(files));
@@ -1033,7 +1554,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
self.qweb_mutex.exec(function() {
return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
if (!xml) { return; }
- openerp.web.qweb.add_template(_.str.trim(xml));
+ instance.web.qweb.add_template(_.str.trim(xml));
});
});
});
@@ -1044,10 +1565,10 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
var mod = this.module_list[j];
if(this.module_loaded[mod])
continue;
- openerp[mod] = {};
+ instance[mod] = {};
// init module mod
- if(openerp._openerp[mod] != undefined) {
- openerp._openerp[mod](openerp);
+ if(instance._openerp[mod] != undefined) {
+ instance._openerp[mod](instance);
this.module_loaded[mod] = true;
}
}
@@ -1167,164 +1688,10 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
}
});
-/**
- * Base class for all visual components. Provides a lot of functionalities helpful
- * for the management of a part of the DOM.
- *
- * Widget handles:
- * - Rendering with QWeb.
- * - Life-cycle management and parenting (when a parent is destroyed, all its children are
- * destroyed too).
- * - Insertion in DOM.
- *
- * Guide to create implementations of the Widget class:
- * ==============================================
- *
- * Here is a sample child class:
- *
- * MyWidget = openerp.base.Widget.extend({
- * // the name of the QWeb template to use for rendering
- * template: "MyQWebTemplate",
- *
- * init: function(parent) {
- * this._super(parent);
- * // stuff that you want to init before the rendering
- * },
- * start: function() {
- * // stuff you want to make after the rendering, `this.$element` holds a correct value
- * this.$element.find(".my_button").click(/* an example of event binding * /);
- *
- * // if you have some asynchronous operations, it's a good idea to return
- * // a promise in start()
- * var promise = this.rpc(...);
- * return promise;
- * }
- * });
- *
- * Now this class can simply be used with the following syntax:
- *
- * var my_widget = new MyWidget(this);
- * my_widget.appendTo($(".some-div"));
- *
- * With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
- * DOM inside the ".some-div" div and its events were binded.
- *
- * And of course, when you don't need that widget anymore, just do:
- *
- * my_widget.destroy();
- *
- * That will kill the widget in a clean way and erase its content from the dom.
- */
-openerp.web.Widget = nova.Widget.extend(_.extend({}, openerp.web.CallbackEnabledMixin, {
- /**
- * The name of the QWeb template that will be used for rendering. Must be
- * redefined in subclasses or the default render() method can not be used.
- *
- * @type string
- */
- template: null,
- /**
- * Constructs the widget and sets its parent if a parent is given.
- *
- * @constructs openerp.web.Widget
- * @extends openerp.web.CallbackEnabled
- *
- * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
- * When that widget is destroyed by calling destroy(), the current instance will be
- * destroyed too. Can be null.
- * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
- * to bind the current Widget to an already existing part of the DOM, which is not compatible
- * with the DOM insertion methods provided by the current implementation of Widget. So
- * for new components this argument should not be provided any more.
- */
- init: function(parent) {
- this._super(parent);
- openerp.web.CallbackEnabledMixin.init.call(this);
- this.session = openerp.connection;
- },
- /**
- * Renders the element. The default implementation renders the widget using QWeb,
- * `this.template` must be defined. The context given to QWeb contains the "widget"
- * key that references `this`.
- */
- renderElement: function() {
- var rendered = null;
- if (this.template)
- rendered = openerp.web.qweb.render(this.template, {widget: this});
- if (_.str.trim(rendered)) {
- var elem = $(rendered);
- this.$element.replaceWith(elem);
- this.$element = elem;
- }
- },
- /**
- * Informs the action manager to do an action. This supposes that
- * the action manager can be found amongst the ancestors of the current widget.
- * If that's not the case this method will simply return `false`.
- */
- do_action: function(action, on_finished) {
- if (this.getParent()) {
- return this.getParent().do_action(action, on_finished);
- }
- return false;
- },
- do_notify: function() {
- if (this.getParent()) {
- return this.getParent().do_notify.apply(this,arguments);
- }
- return false;
- },
- do_warn: function() {
- if (this.getParent()) {
- return this.getParent().do_warn.apply(this,arguments);
- }
- return false;
- },
- rpc: function(url, data, success, error) {
- var def = $.Deferred().then(success, error);
- var self = this;
- openerp.connection.rpc(url, data). then(function() {
- if (!self.isDestroyed())
- def.resolve.apply(def, arguments);
- }, function() {
- if (!self.isDestroyed())
- def.reject.apply(def, arguments);
- });
- return def.promise();
- }
-}));
-
-/**
- * @deprecated use :class:`openerp.web.Widget`
- */
-openerp.web.OldWidget = openerp.web.Widget.extend({
- init: function(parent, element_id) {
- this._super(parent);
- this.element_id = element_id;
- this.element_id = this.element_id || _.uniqueId('widget-');
- var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
- },
- renderElement: function() {
- var rendered = this.render();
- if (rendered) {
- var elem = $(rendered);
- this.$element.replaceWith(elem);
- this.$element = elem;
- }
- return this;
- },
- render: function (additional) {
- if (this.template)
- return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
- return null;
- }
-});
-
-openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.web.TranslationDataBase# */{
+instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
/**
- * @constructs openerp.web.TranslationDataBase
- * @extends openerp.web.Class
+ * @constructs instance.web.TranslationDataBase
+ * @extends instance.web.Class
*/
init: function() {
this.db = {};
@@ -1374,138 +1741,33 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
}
});
-/** Configure blockui */
-if ($.blockUI) {
- $.blockUI.defaults.baseZ = 1100;
- $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
-}
-
-/** Configure default qweb */
-openerp.web._t = new openerp.web.TranslationDataBase().build_translation_function();
-/**
- * Lazy translation function, only performs the translation when actually
- * printed (e.g. inserted into a template)
- *
- * Useful when defining translatable strings in code evaluated before the
- * translation database is loaded, as class attributes or at the top-level of
- * an OpenERP Web module
- *
- * @param {String} s string to translate
- * @returns {Object} lazy translation object
- */
-openerp.web._lt = function (s) {
- return {toString: function () { return openerp.web._t(s); }}
-};
-openerp.web.qweb = new QWeb2.Engine();
-openerp.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
-openerp.web.qweb.default_dict = {
- '_' : _,
- '_t' : openerp.web._t
-};
-openerp.web.qweb.preprocess_node = function() {
- // Note that 'this' is the Qweb Node
- switch (this.node.nodeType) {
- case 3:
- case 4:
- // Text and CDATAs
- var translation = this.node.parentNode.attributes['t-translation'];
- if (translation && translation.value === 'off') {
- return;
- }
- var ts = _.str.trim(this.node.data);
- if (ts.length === 0) {
- return;
- }
- var tr = openerp.web._t(ts);
- if (tr !== ts) {
- this.node.data = tr;
- }
- break;
- case 1:
- // Element
- var attr, attrs = ['label', 'title', 'alt'];
- while (attr = attrs.pop()) {
- if (this.attributes[attr]) {
- this.attributes[attr] = openerp.web._t(this.attributes[attr]);
- }
- }
- }
-};
-
/**
- * A small utility function to check if a class implements correctly an interface, assuming that
- * interface is simply specified using a dictionary containing methods and attributes with the
- * correct type. It only performs the check when in debug mode and the only effect of an invalid
- * check is messages in the console.
+ * @deprecated use :class:`instance.web.Widget`
*/
-openerp.web.check_interface = function(_class, _interface) {
- if (! openerp.web.check_interface.debug)
- return;
- for (var member in _interface) {
- if ( (typeof _class.prototype[member] != typeof _interface[member]) ) {
- console.error("class failed to implement interface member '" + member + "'");
+instance.web.OldWidget = instance.web.Widget.extend({
+ init: function(parent, element_id) {
+ this._super(parent);
+ this.element_id = element_id;
+ this.element_id = this.element_id || _.uniqueId('widget-');
+ var tmp = document.getElementById(this.element_id);
+ this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
+ },
+ renderElement: function() {
+ var rendered = this.render();
+ if (rendered) {
+ var elem = $(rendered);
+ this.$element.replaceWith(elem);
+ this.$element = elem;
}
+ return this;
+ },
+ render: function (additional) {
+ if (this.template)
+ return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
+ return null;
}
-}
-openerp.web.check_interface.debug = ($.deparam($.param.querystring()).debug != undefined);
-
-/** Jquery extentions */
-$.Mutex = (function() {
- function Mutex() {
- this.def = $.Deferred().resolve();
- }
- Mutex.prototype.exec = function(action) {
- var current = this.def;
- var next = this.def = $.Deferred();
- return current.pipe(function() {
- return $.when(action()).always(function() {
- next.resolve();
- });
- });
- };
- return Mutex;
-})();
-
-/** Setup default connection */
-openerp.connection = new openerp.web.Connection();
-openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
-
-
-$.async_when = function() {
- var async = false;
- var def = $.Deferred();
- $.when.apply($, arguments).then(function() {
- var args = arguments;
- var action = function() {
- def.resolve.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- }, function() {
- var args = arguments;
- var action = function() {
- def.reject.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- });
- async = true;
- return def;
-};
-
-// special tweak for the web client
-var old_async_when = $.async_when;
-$.async_when = function() {
- if (openerp.connection.synch)
- return $.when.apply(this, arguments);
- else
- return old_async_when.apply(this, arguments);
-};
+});
-};
+}
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
new file mode 100644
index 00000000000..86040302668
--- /dev/null
+++ b/addons/web/static/src/js/coresetup.js
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------
+ * OpenERP Web core
+ *--------------------------------------------------------*/
+var console;
+if (!console) {
+ console = {log: function () {}};
+}
+if (!console.debug) {
+ console.debug = console.log;
+}
+
+openerp.web.coresetup = function(instance) {
+
+/** Configure default qweb */
+instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
+/**
+ * Lazy translation function, only performs the translation when actually
+ * printed (e.g. inserted into a template)
+ *
+ * Useful when defining translatable strings in code evaluated before the
+ * translation database is loaded, as class attributes or at the top-level of
+ * an OpenERP Web module
+ *
+ * @param {String} s string to translate
+ * @returns {Object} lazy translation object
+ */
+instance.web._lt = function (s) {
+ return {toString: function () { return instance.web._t(s); }}
+};
+instance.web.qweb = new QWeb2.Engine();
+instance.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
+instance.web.qweb.default_dict = {
+ '_' : _,
+ '_t' : instance.web._t
+};
+instance.web.qweb.preprocess_node = function() {
+ // Note that 'this' is the Qweb Node
+ switch (this.node.nodeType) {
+ case 3:
+ case 4:
+ // Text and CDATAs
+ var translation = this.node.parentNode.attributes['t-translation'];
+ if (translation && translation.value === 'off') {
+ return;
+ }
+ var ts = _.str.trim(this.node.data);
+ if (ts.length === 0) {
+ return;
+ }
+ var tr = instance.web._t(ts);
+ if (tr !== ts) {
+ this.node.data = tr;
+ }
+ break;
+ case 1:
+ // Element
+ var attr, attrs = ['label', 'title', 'alt'];
+ while (attr = attrs.pop()) {
+ if (this.attributes[attr]) {
+ this.attributes[attr] = instance.web._t(this.attributes[attr]);
+ }
+ }
+ }
+};
+
+/** Configure blockui */
+if ($.blockUI) {
+ $.blockUI.defaults.baseZ = 1100;
+ $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
+}
+
+/** Custom jQuery plugins */
+$.fn.getAttributes = function() {
+ var o = {};
+ if (this.length) {
+ for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) {
+ attr = attrs.item(i)
+ o[attr.nodeName] = attr.nodeValue;
+ }
+ }
+ return o;
+}
+
+/** Jquery extentions */
+$.Mutex = (function() {
+ function Mutex() {
+ this.def = $.Deferred().resolve();
+ }
+ Mutex.prototype.exec = function(action) {
+ var current = this.def;
+ var next = this.def = $.Deferred();
+ return current.pipe(function() {
+ return $.when(action()).always(function() {
+ next.resolve();
+ });
+ });
+ };
+ return Mutex;
+})();
+
+/** Setup default connection */
+instance.connection = new instance.web.Connection();
+instance.web.qweb.default_dict['__debug__'] = instance.connection.debug;
+
+$.async_when = function() {
+ var async = false;
+ var def = $.Deferred();
+ $.when.apply($, arguments).then(function() {
+ var args = arguments;
+ var action = function() {
+ def.resolve.apply(def, args);
+ };
+ if (async)
+ action();
+ else
+ setTimeout(action, 0);
+ }, function() {
+ var args = arguments;
+ var action = function() {
+ def.reject.apply(def, args);
+ };
+ if (async)
+ action();
+ else
+ setTimeout(action, 0);
+ });
+ async = true;
+ return def;
+};
+
+// special tweak for the web client
+var old_async_when = $.async_when;
+$.async_when = function() {
+ if (instance.connection.synch)
+ return $.when.apply(this, arguments);
+ else
+ return old_async_when.apply(this, arguments);
+};
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index a29a9c5ad36..2dc66aaa135 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -1,5 +1,5 @@
-openerp.web.data = function(openerp) {
+openerp.web.data = function(instance) {
/**
* Serializes the sort criterion array of a dataset into a form which can be
@@ -8,7 +8,7 @@ openerp.web.data = function(openerp) {
* @param {Array} criterion array of fields, from first to last criteria, prefixed with '-' for reverse sorting
* @returns {String} SQL-like sorting string (``ORDER BY``) clause
*/
-openerp.web.serialize_sort = function (criterion) {
+instance.web.serialize_sort = function (criterion) {
return _.map(criterion,
function (criteria) {
if (criteria[0] === '-') {
@@ -18,7 +18,7 @@ openerp.web.serialize_sort = function (criterion) {
}).join(', ');
};
-openerp.web.Query = openerp.web.Class.extend({
+instance.web.Query = instance.web.Class.extend({
init: function (model, fields) {
this._model = model;
this._fields = fields;
@@ -30,7 +30,7 @@ openerp.web.Query = openerp.web.Class.extend({
},
clone: function (to_set) {
to_set = to_set || {};
- var q = new openerp.web.Query(this._model, this._fields);
+ var q = new instance.web.Query(this._model, this._fields);
q._context = this._context;
q._filter = this._filter;
q._limit = this._limit;
@@ -41,11 +41,11 @@ openerp.web.Query = openerp.web.Class.extend({
if (!to_set.hasOwnProperty(key)) { continue; }
switch(key) {
case 'filter':
- q._filter = new openerp.web.CompoundDomain(
+ q._filter = new instance.web.CompoundDomain(
q._filter, to_set.filter);
break;
case 'context':
- q._context = new openerp.web.CompoundContext(
+ q._context = new instance.web.CompoundContext(
q._context, to_set.context);
break;
case 'limit':
@@ -58,14 +58,14 @@ openerp.web.Query = openerp.web.Class.extend({
},
_execute: function () {
var self = this;
- return openerp.connection.rpc('/web/dataset/search_read', {
+ return instance.connection.rpc('/web/dataset/search_read', {
model: this._model.name,
fields: this._fields || false,
domain: this._model.domain(this._filter),
context: this._model.context(this._context),
offset: this._offset,
limit: this._limit,
- sort: openerp.web.serialize_sort(this._order_by)
+ sort: instance.web.serialize_sort(this._order_by)
}).pipe(function (results) {
self._count = results.length;
return results.records;
@@ -127,10 +127,10 @@ openerp.web.Query = openerp.web.Class.extend({
context: this._model.context(this._context),
offset: this._offset,
limit: this._limit,
- orderby: openerp.web.serialize_sort(this._order_by) || false
+ orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
return _(results).map(function (result) {
- return new openerp.web.data.Group(
+ return new instance.web.data.Group(
self._model.name, grouping[0], result);
});
});
@@ -194,10 +194,10 @@ openerp.web.Query = openerp.web.Class.extend({
}
});
-openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
+instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
/**
- * @constructs openerp.web.Model
- * @extends openerp.web.Class
+ * @constructs instance.web.Model
+ * @extends instance.web.Class
*
* @param {String} model_name name of the OpenERP model this object is bound to
* @param {Object} [context]
@@ -233,7 +233,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
kwargs = args;
args = [];
}
- return openerp.connection.rpc('/web/dataset/call_kw', {
+ return instance.connection.rpc('/web/dataset/call_kw', {
model: this.name,
method: method,
args: args,
@@ -247,7 +247,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
* @returns {openerp.web.Query}
*/
query: function (fields) {
- return new openerp.web.Query(this, fields);
+ return new instance.web.Query(this, fields);
},
/**
* Executes a signal on the designated workflow, on the bound OpenERP model
@@ -256,7 +256,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
* @param {String} signal signal to trigger on the workflow
*/
exec_workflow: function (id, signal) {
- return openerp.connection.rpc('/web/dataset/exec_workflow', {
+ return instance.connection.rpc('/web/dataset/exec_workflow', {
model: this.name,
id: id,
signal: signal
@@ -270,7 +270,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
*/
domain: function (domain) {
if (!domain) { return this._domain; }
- return new openerp.web.CompoundDomain(
+ return new instance.web.CompoundDomain(
this._domain, domain);
},
/**
@@ -281,8 +281,8 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
* @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.
*/
context: function (context) {
- return new openerp.web.CompoundContext(
- openerp.connection.user_context, this._context, context || {});
+ return new instance.web.CompoundContext(
+ instance.connection.user_context, this._context, context || {});
},
/**
* Button action caller, needs to perform cleanup if an action is returned
@@ -292,7 +292,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
* FIXME: remove when evaluator integrated
*/
call_button: function (method, args) {
- return openerp.connection.rpc('/web/dataset/call_button', {
+ return instance.connection.rpc('/web/dataset/call_button', {
model: this.name,
method: method,
domain_id: null,
@@ -302,12 +302,12 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{
},
});
-openerp.web.Traverser = openerp.web.Class.extend(/** @lends openerp.web.Traverser# */{
+instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{
/**
- * @constructs openerp.web.Traverser
- * @extends openerp.web.Class
+ * @constructs instance.web.Traverser
+ * @extends instance.web.Class
*
- * @param {openerp.web.Model} model instance this traverser is bound to
+ * @param {instance.web.Model} model instance this traverser is bound to
*/
init: function (model) {
this._model = model;
@@ -387,11 +387,11 @@ openerp.web.Traverser = openerp.web.Class.extend(/** @lends openerp.web.Traverse
*
* @namespace
*/
-openerp.web.data = {
- Group: openerp.web.Class.extend(/** @lends openerp.web.data.Group# */{
+instance.web.data = {
+ Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{
/**
- * @constructs openerp.web.data.Group
- * @extends openerp.web.Class
+ * @constructs instance.web.data.Group
+ * @extends instance.web.Class
*/
init: function (model, grouping_field, read_group_group) {
// In cases where group_by_no_leaf and no group_by, the result of
@@ -411,7 +411,7 @@ openerp.web.data = {
aggregates[key] = value || 0;
});
- this.model = new openerp.web.Model(
+ this.model = new instance.web.Model(
model, fixed_group.__context, fixed_group.__domain);
var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
@@ -439,7 +439,7 @@ openerp.web.data = {
})
};
-openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{
+instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{
/**
* Management interface between views and grouped collections of OpenERP
* records.
@@ -447,13 +447,13 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da
* The root DataGroup is instantiated with the relevant information
* (a session, a model, a domain, a context and a group_by sequence), the
* domain and context may be empty. It is then interacted with via
- * :js:func:`~openerp.web.DataGroup.list`, which is used to read the
+ * :js:func:`~instance.web.DataGroup.list`, which is used to read the
* content of the current grouping level.
*
- * @constructs openerp.web.DataGroup
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.DataGroup
+ * @extends instance.web.OldWidget
*
- * @param {openerp.web.OldWidget} parent widget
+ * @param {instance.web.OldWidget} parent widget
* @param {String} model name of the model managed by this DataGroup
* @param {Array} domain search domain for this DataGroup
* @param {Object} context context of the DataGroup's searches
@@ -462,7 +462,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da
*/
init: function(parent, model, domain, context, group_by, level) {
this._super(parent, null);
- this.model = new openerp.web.Model(model, context, domain);
+ this.model = new instance.web.Model(model, context, domain);
this.group_by = group_by;
this.context = context;
this.domain = domain;
@@ -476,7 +476,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da
.group_by(this.group_by)).then(function (groups) {
if (!groups) {
ifRecords(_.extend(
- new openerp.web.DataSetSearch(
+ new instance.web.DataSetSearch(
self, self.model.name,
self.model.context(),
self.model.domain()),
@@ -487,7 +487,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da
var child_context = _.extend(
{}, self.model.context(), group.model.context());
return _.extend(
- new openerp.web.DataGroup(
+ new instance.web.DataGroup(
self, self.model.name, group.model.domain(),
child_context, group.model._context.group_by,
self.level + 1),
@@ -504,16 +504,16 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da
});
}
});
-openerp.web.ContainerDataGroup = openerp.web.DataGroup.extend({ });
-openerp.web.GrouplessDataGroup = openerp.web.DataGroup.extend({ });
+instance.web.ContainerDataGroup = instance.web.DataGroup.extend({ });
+instance.web.GrouplessDataGroup = instance.web.DataGroup.extend({ });
-openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ {
+instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ {
/**
* A specialization of groupless data groups, relying on a single static
* dataset as its records provider.
*
- * @constructs openerp.web.StaticDataGroup
- * @extends openerp.web.GrouplessDataGroup
+ * @constructs instance.web.StaticDataGroup
+ * @extends instance.web.GrouplessDataGroup
* @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
*/
init: function (dataset) {
@@ -524,13 +524,13 @@ openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends
}
});
-openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{
+instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{
/**
* DateaManagement interface between views and the collection of selected
* OpenERP records (represents the view's state?)
*
- * @constructs openerp.web.DataSet
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.DataSet
+ * @extends instance.web.OldWidget
*
* @param {String} model the OpenERP model this dataset will manage
*/
@@ -540,7 +540,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data
this.context = context || {};
this.index = null;
this._sort = [];
- this._model = new openerp.web.Model(model, context);
+ this._model = new instance.web.Model(model, context);
},
previous: function () {
this.index -= 1;
@@ -789,7 +789,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data
*/
sort: function (field, force_reverse) {
if (!field) {
- return openerp.web.serialize_sort(this._sort);
+ return instance.web.serialize_sort(this._sort);
}
var reverse = force_reverse || (this._sort[0] === field);
this._sort.splice.apply(
@@ -806,7 +806,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data
this.ids = n_ids;
},
});
-openerp.web.DataSetStatic = openerp.web.DataSet.extend({
+instance.web.DataSetStatic = instance.web.DataSet.extend({
init: function(parent, model, context, ids) {
this._super(parent, model, context);
// all local records
@@ -836,10 +836,10 @@ openerp.web.DataSetStatic = openerp.web.DataSet.extend({
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});
-openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{
+instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{
/**
- * @constructs openerp.web.DataSetSearch
- * @extends openerp.web.DataSet
+ * @constructs instance.web.DataSetSearch
+ * @extends instance.web.DataSet
*
* @param {Object} parent
* @param {String} model
@@ -851,7 +851,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
this.domain = domain || [];
this._length = null;
this.ids = [];
- this._model = new openerp.web.Model(model, context, domain);
+ this._model = new instance.web.Model(model, context, domain);
},
/**
* Read a slice of the records represented by this DataSet, based on its
@@ -906,7 +906,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
return this._super();
}
});
-openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
+instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
virtual_id_prefix: "one2many_v_id_",
debug_mode: true,
init: function() {
@@ -1071,9 +1071,9 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
this.on_change();
},
});
-openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
+instance.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
-openerp.web.ProxyDataSet = openerp.web.DataSetSearch.extend({
+instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
init: function() {
this._super.apply(this, arguments);
this.create_function = null;
@@ -1119,7 +1119,7 @@ openerp.web.ProxyDataSet = openerp.web.DataSetSearch.extend({
on_unlink: function(ids) {}
});
-openerp.web.CompoundContext = openerp.web.Class.extend({
+instance.web.CompoundContext = instance.web.Class.extend({
init: function () {
this.__ref = "compound_context";
this.__contexts = [];
@@ -1142,7 +1142,7 @@ openerp.web.CompoundContext = openerp.web.Class.extend({
}
});
-openerp.web.CompoundDomain = openerp.web.Class.extend({
+instance.web.CompoundDomain = instance.web.Class.extend({
init: function () {
this.__ref = "compound_domain";
this.__domains = [];
@@ -1165,10 +1165,10 @@ openerp.web.CompoundDomain = openerp.web.Class.extend({
}
});
-openerp.web.DropMisordered = openerp.web.Class.extend(/** @lends openerp.web.DropMisordered# */{
+instance.web.DropMisordered = instance.web.Class.extend(/** @lends openerp.web.DropMisordered# */{
/**
- * @constructs openerp.web.DropMisordered
- * @extends openerp.web.Class
+ * @constructs instance.web.DropMisordered
+ * @extends instance.web.Class
*
* @param {Boolean} [failMisordered=false] whether mis-ordered responses should be failed or just ignored
*/
diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js
index 3d15a4e3607..ebe82ddb4b4 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -1,14 +1,14 @@
-openerp.web.data_export = function(openerp) {
-var QWeb = openerp.web.qweb,
- _t = openerp.web._t;
-openerp.web.DataExport = openerp.web.Dialog.extend({
+openerp.web.data_export = function(instance) {
+var QWeb = instance.web.qweb,
+ _t = instance.web._t;
+instance.web.DataExport = instance.web.Dialog.extend({
template: 'ExportTreeView',
dialog_title: {toString: function () { return _t("Export Data"); }},
init: function(parent, dataset) {
this._super(parent);
this.records = {};
this.dataset = dataset;
- this.exports = new openerp.web.DataSetSearch(
+ this.exports = new instance.web.DataSetSearch(
this, 'ir.exports', this.dataset.get_context());
},
start: function() {
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
index 3054a7784c4..30f9323f723 100644
--- a/addons/web/static/src/js/data_import.js
+++ b/addons/web/static/src/js/data_import.js
@@ -1,6 +1,6 @@
-openerp.web.data_import = function(openerp) {
-var QWeb = openerp.web.qweb,
- _t = openerp.web._t;
+openerp.web.data_import = function(instance) {
+var QWeb = instance.web.qweb,
+ _t = instance.web._t;
/**
* Safari does not deal well at all with raw JSON data being returned. As a
* result, we're going to cheat by using a pseudo-jsonp: instead of getting
@@ -29,7 +29,7 @@ function jsonp(form, attributes, callback) {
$(form).ajaxSubmit(attributes);
}
-openerp.web.DataImport = openerp.web.Dialog.extend({
+instance.web.DataImport = instance.web.Dialog.extend({
template: 'ImportDataView',
dialog_title: {toString: function () { return _t("Import Data"); }},
init: function(parent, dataset){
@@ -79,10 +79,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
this.$element.delegate('fieldset legend', 'click', function() {
$(this).parent().toggleClass('oe-closed');
});
- this.ready.push(new openerp.web.DataSet(this, this.model).call(
+ this.ready.push(new instance.web.DataSet(this, this.model).call(
'fields_get', [], function (fields) {
self.graft_fields(fields);
- self.ready.push(new openerp.web.DataSet(self, self.model)
+ self.ready.push(new instance.web.DataSet(self, self.model)
.default_get(_.pluck(self.fields, 'id')).then(function (fields) {
_.each(fields, function(val, key) {
if (val) {
@@ -140,7 +140,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
f.fields = [];
// only fetch sub-fields to a depth of 2 levels
if (level < 2) {
- self.ready.push(new openerp.web.DataSet(self, field.relation).call(
+ self.ready.push(new instance.web.DataSet(self, field.relation).call(
'fields_get', [], function (fields) {
self.graft_fields(fields, f, level+1);
}));
@@ -151,7 +151,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
});
},
toggle_import_button: function (newstate) {
- openerp.web.dialog(this.$element, 'widget')
+ instance.web.dialog(this.$element, 'widget')
.find('.oe-dialog-import-button')
.button('option', 'disabled', !newstate);
},
diff --git a/addons/web/static/src/js/dates.js b/addons/web/static/src/js/dates.js
index d61223113f8..3db48c8412a 100644
--- a/addons/web/static/src/js/dates.js
+++ b/addons/web/static/src/js/dates.js
@@ -1,5 +1,5 @@
-openerp.web.dates = function(openerp) {
+openerp.web.dates = function(instance) {
/**
* Converts a string to a Date javascript object using OpenERP's
@@ -11,7 +11,7 @@ openerp.web.dates = function(openerp) {
* @param {String} str A string representing a datetime.
* @returns {Date}
*/
-openerp.web.str_to_datetime = function(str) {
+instance.web.str_to_datetime = function(str) {
if(!str) {
return str;
}
@@ -38,7 +38,7 @@ openerp.web.str_to_datetime = function(str) {
* @param {String} str A string representing a date.
* @returns {Date}
*/
-openerp.web.str_to_date = function(str) {
+instance.web.str_to_date = function(str) {
if(!str) {
return str;
}
@@ -65,7 +65,7 @@ openerp.web.str_to_date = function(str) {
* @param {String} str A string representing a time.
* @returns {Date}
*/
-openerp.web.str_to_time = function(str) {
+instance.web.str_to_time = function(str) {
if(!str) {
return str;
}
@@ -104,7 +104,7 @@ var zpad = function(str, size) {
* @param {Date} obj
* @returns {String} A string representing a datetime.
*/
-openerp.web.datetime_to_str = function(obj) {
+instance.web.datetime_to_str = function(obj) {
if (!obj) {
return false;
}
@@ -124,7 +124,7 @@ openerp.web.datetime_to_str = function(obj) {
* @param {Date} obj
* @returns {String} A string representing a date.
*/
-openerp.web.date_to_str = function(obj) {
+instance.web.date_to_str = function(obj) {
if (!obj) {
return false;
}
@@ -143,7 +143,7 @@ openerp.web.date_to_str = function(obj) {
* @param {Date} obj
* @returns {String} A string representing a time.
*/
-openerp.web.time_to_str = function(obj) {
+instance.web.time_to_str = function(obj) {
if (!obj) {
return false;
}
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 53863e63e97..640ba4d5a93 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -1,6 +1,6 @@
-openerp.web.formats = function(openerp) {
-var _t = openerp.web._t;
+openerp.web.formats = function(instance) {
+var _t = instance.web._t;
/**
* Intersperses ``separator`` in ``str`` at the positions indicated by
@@ -23,7 +23,7 @@ var _t = openerp.web._t;
* @param {String} separator
* @returns {String}
*/
-openerp.web.intersperse = function (str, indices, separator) {
+instance.web.intersperse = function (str, indices, separator) {
separator = separator || '';
var result = [], last = str.length;
@@ -55,10 +55,10 @@ openerp.web.intersperse = function (str, indices, separator) {
* @param {String} num
* @returns {String}
*/
-openerp.web.insert_thousand_seps = function (num) {
+instance.web.insert_thousand_seps = function (num) {
var negative = num[0] === '-';
num = (negative ? num.slice(1) : num);
- return (negative ? '-' : '') + openerp.web.intersperse(
+ return (negative ? '-' : '') + instance.web.intersperse(
num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
};
@@ -69,7 +69,7 @@ openerp.web.insert_thousand_seps = function (num) {
*
* @param {String} value original format
*/
-openerp.web.strip_raw_chars = function (value) {
+instance.web.strip_raw_chars = function (value) {
var isletter = /[a-zA-Z]/, output = [];
for(var index=0; index < value.length; ++index) {
var character = value[index];
@@ -81,7 +81,7 @@ openerp.web.strip_raw_chars = function (value) {
return output.join('');
};
var normalize_format = function (format) {
- return Date.normalizeFormat(openerp.web.strip_raw_chars(format));
+ return Date.normalizeFormat(instance.web.strip_raw_chars(format));
};
/**
* Formats a single atomic value based on a field descriptor
@@ -93,7 +93,7 @@ var normalize_format = function (format) {
* @param {Object} [descriptor.digits] used for the formatting of floats
* @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
*/
-openerp.web.format_value = function (value, descriptor, value_if_empty) {
+instance.web.format_value = function (value, descriptor, value_if_empty) {
// If NaN value, display as with a `false` (empty cell)
if (typeof value === 'number' && isNaN(value)) {
value = false;
@@ -111,16 +111,16 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
return value_if_empty === undefined ? '' : value_if_empty;
}
var l10n = _t.database.parameters;
- switch (descriptor.widget || descriptor.type) {
+ switch (descriptor.type || (descriptor.field && descriptor.field.type)) {
case 'id':
return value.toString();
case 'integer':
- return openerp.web.insert_thousand_seps(
+ return instance.web.insert_thousand_seps(
_.str.sprintf('%d', value));
case 'float':
var precision = descriptor.digits ? descriptor.digits[1] : 2;
var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
- formatted[0] = openerp.web.insert_thousand_seps(formatted[0]);
+ formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
case 'float_time':
var pattern = '%02d:%02d';
@@ -136,17 +136,17 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
return value[1];
case 'datetime':
if (typeof(value) == "string")
- value = openerp.web.auto_str_to_date(value);
+ value = instance.web.auto_str_to_date(value);
return value.toString(normalize_format(l10n.date_format)
+ ' ' + normalize_format(l10n.time_format));
case 'date':
if (typeof(value) == "string")
- value = openerp.web.auto_str_to_date(value);
+ value = instance.web.auto_str_to_date(value);
return value.toString(normalize_format(l10n.date_format));
case 'time':
if (typeof(value) == "string")
- value = openerp.web.auto_str_to_date(value);
+ value = instance.web.auto_str_to_date(value);
return value.toString(normalize_format(l10n.time_format));
case 'selection': case 'statusbar':
// Each choice is [value, label]
@@ -163,7 +163,7 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
}
};
-openerp.web.parse_value = function (value, descriptor, value_if_empty) {
+instance.web.parse_value = function (value, descriptor, value_if_empty) {
var date_pattern = normalize_format(_t.database.parameters.date_format),
time_pattern = normalize_format(_t.database.parameters.time_format);
switch (value) {
@@ -171,12 +171,12 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
case "":
return value_if_empty === undefined ? false : value_if_empty;
}
- switch (descriptor.widget || descriptor.type) {
+ switch (descriptor.type || (descriptor.field && descriptor.field.type)) {
case 'integer':
var tmp;
do {
tmp = value;
- value = value.replace(openerp.web._t.database.parameters.thousands_sep, "");
+ value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
} while(tmp !== value);
tmp = Number(value);
if (isNaN(tmp))
@@ -190,9 +190,9 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
var tmp2 = value;
do {
tmp = tmp2;
- tmp2 = tmp.replace(openerp.web._t.database.parameters.thousands_sep, "");
+ tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
} while(tmp !== tmp2);
- var reformatted_value = tmp.replace(openerp.web._t.database.parameters.decimal_point, ".");
+ var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
var parsed = Number(reformatted_value);
if (isNaN(parsed))
throw new Error(value + " is not a correct float");
@@ -205,62 +205,62 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
}
var float_time_pair = value.split(":");
if (float_time_pair.length != 2)
- return factor * openerp.web.parse_value(value, {type: "float"});
- var hours = openerp.web.parse_value(float_time_pair[0], {type: "integer"});
- var minutes = openerp.web.parse_value(float_time_pair[1], {type: "integer"});
+ return factor * instance.web.parse_value(value, {type: "float"});
+ var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
+ var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
return factor * (hours + (minutes / 60));
case 'progressbar':
- return openerp.web.parse_value(value, {type: "float"});
+ return instance.web.parse_value(value, {type: "float"});
case 'datetime':
var datetime = Date.parseExact(
value, (date_pattern + ' ' + time_pattern));
if (datetime !== null)
- return openerp.web.datetime_to_str(datetime);
+ return instance.web.datetime_to_str(datetime);
datetime = Date.parse(value);
if (datetime !== null)
- return openerp.web.datetime_to_str(datetime);
+ return instance.web.datetime_to_str(datetime);
throw new Error(value + " is not a valid datetime");
case 'date':
var date = Date.parseExact(value, date_pattern);
if (date !== null)
- return openerp.web.date_to_str(date);
+ return instance.web.date_to_str(date);
date = Date.parse(value);
if (date !== null)
- return openerp.web.date_to_str(date);
+ return instance.web.date_to_str(date);
throw new Error(value + " is not a valid date");
case 'time':
var time = Date.parseExact(value, time_pattern);
if (time !== null)
- return openerp.web.time_to_str(time);
+ return instance.web.time_to_str(time);
time = Date.parse(value);
if (time !== null)
- return openerp.web.time_to_str(time);
+ return instance.web.time_to_str(time);
throw new Error(value + " is not a valid time");
}
return value;
};
-openerp.web.auto_str_to_date = function(value, type) {
+instance.web.auto_str_to_date = function(value, type) {
try {
- return openerp.web.str_to_datetime(value);
+ return instance.web.str_to_datetime(value);
} catch(e) {}
try {
- return openerp.web.str_to_date(value);
+ return instance.web.str_to_date(value);
} catch(e) {}
try {
- return openerp.web.str_to_time(value);
+ return instance.web.str_to_time(value);
} catch(e) {}
throw new Error("'" + value + "' is not a valid date, datetime nor time");
};
-openerp.web.auto_date_to_str = function(value, type) {
+instance.web.auto_date_to_str = function(value, type) {
switch(type) {
case 'datetime':
- return openerp.web.datetime_to_str(value);
+ return instance.web.datetime_to_str(value);
case 'date':
- return openerp.web.date_to_str(value);
+ return instance.web.date_to_str(value);
case 'time':
- return openerp.web.time_to_str(value);
+ return instance.web.time_to_str(value);
default:
throw new Error(type + " is not convertible to date, datetime nor time");
}
@@ -290,7 +290,7 @@ openerp.web.auto_date_to_str = function(value, type) {
* @param {Number} [options.id] current record's id
*
*/
-openerp.web.format_cell = function (row_data, column, options) {
+instance.web.format_cell = function (row_data, column, options) {
options = options || {};
var attrs = {};
if (options.process_modifiers !== false) {
@@ -303,9 +303,9 @@ openerp.web.format_cell = function (row_data, column, options) {
'<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
'</button>', {
title: column.string || '',
- additional_attributes: isNaN(row_data["id"].value) && openerp.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
+ additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
'disabled="disabled" class="oe-listview-button-disabled"' : '',
- prefix: openerp.connection.prefix,
+ prefix: instance.connection.prefix,
icon: column.icon,
alt: column.string || ''
});
@@ -320,11 +320,11 @@ openerp.web.format_cell = function (row_data, column, options) {
row_data[column.id].value ? 'checked="checked"' : '');
case "binary":
var text = _t("Download"),
- download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', openerp.connection.session_id, options.model, column.id, options.id);
+ download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id);
if (column.filename) {
download_url += '&filename_field=' + column.filename;
if (row_data[column.filename]) {
- text = _.str.sprintf(_t("Download \"%s\""), openerp.web.format_value(
+ text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
row_data[column.filename].value, {type: 'char'}));
}
}
@@ -340,7 +340,7 @@ openerp.web.format_cell = function (row_data, column, options) {
});
}
- return _.escape(openerp.web.format_value(
+ return _.escape(instance.web.format_value(
row_data[column.id].value, column, options.value_if_empty));
}
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 773c10382f6..c2df866b5c6 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -1,13 +1,80 @@
-openerp.web.search = function(openerp) {
-var QWeb = openerp.web.qweb,
- _t = openerp.web._t,
- _lt = openerp.web._lt;
+openerp.web.search = function(instance) {
+var QWeb = instance.web.qweb,
+ _t = instance.web._t,
+ _lt = instance.web._lt;
+_.mixin({
+ sum: function (obj) { return _.reduce(obj, function (a, b) { return a + b; }, 0); }
+});
+
+// Have SearchBox optionally use callback function to produce inputs and facets
+// (views) set on callbacks.make_facet and callbacks.make_input keys when
+// initializing VisualSearch
+var SearchBox_renderFacet = function (facet, position) {
+ var view = new (this.app.options.callbacks['make_facet'] || VS.ui.SearchFacet)({
+ app : this.app,
+ model : facet,
+ order : position
+ });
-openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.SearchView# */{
- template: "EmptyComponent",
+ // Input first, facet second.
+ this.renderSearchInput();
+ this.facetViews.push(view);
+ this.$('.VS-search-inner').children().eq(position*2).after(view.render().el);
+
+ view.calculateSize();
+ _.defer(_.bind(view.calculateSize, view));
+
+ return view;
+ }; // warning: will not match
+// Ensure we're replacing the function we think
+if (SearchBox_renderFacet.toString() !== VS.ui.SearchBox.prototype.renderFacet.toString().replace(/(VS\.ui\.SearchFacet)/, "(this.app.options.callbacks['make_facet'] || $1)")) {
+ throw new Error(
+ "Trying to replace wrong version of VS.ui.SearchBox#renderFacet. "
+ + "Please fix replacement.");
+}
+var SearchBox_renderSearchInput = function () {
+ var input = new (this.app.options.callbacks['make_input'] || VS.ui.SearchInput)({position: this.inputViews.length, app: this.app});
+ this.$('.VS-search-inner').append(input.render().el);
+ this.inputViews.push(input);
+ };
+// Ensure we're replacing the function we think
+if (SearchBox_renderSearchInput.toString() !== VS.ui.SearchBox.prototype.renderSearchInput.toString().replace(/(VS\.ui\.SearchInput)/, "(this.app.options.callbacks['make_input'] || $1)")) {
+ throw new Error(
+ "Trying to replace wrong version of VS.ui.SearchBox#renderSearchInput. "
+ + "Please fix replacement.");
+}
+var SearchBox_searchEvent = function (e) {
+ var query = null;
+ this.renderFacets();
+ this.focusSearch(e);
+ this.app.options.callbacks.search(query, this.app.searchQuery);
+ };
+if (SearchBox_searchEvent.toString() !== VS.ui.SearchBox.prototype.searchEvent.toString().replace(
+ /this\.value\(\);\n[ ]{4}this\.focusSearch\(e\);\n[ ]{4}this\.value\(query\)/,
+ 'null;\n this.renderFacets();\n this.focusSearch(e)')) {
+ throw new Error(
+ "Trying to replace wrong version of VS.ui.SearchBox#searchEvent. "
+ + "Please fix replacement.");
+}
+_.extend(VS.ui.SearchBox.prototype, {
+ renderFacet: SearchBox_renderFacet,
+ renderSearchInput: SearchBox_renderSearchInput,
+ searchEvent: SearchBox_searchEvent
+});
+_.extend(VS.model.SearchFacet.prototype, {
+ value: function () {
+ if (this.has('json')) {
+ return this.get('json');
+ }
+ return this.get('value');
+ }
+});
+
+instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.SearchView# */{
+ template: "SearchView",
/**
- * @constructs openerp.web.SearchView
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.SearchView
+ * @extends instance.web.OldWidget
*
* @param parent
* @param element_id
@@ -25,9 +92,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
this.has_defaults = !_.isEmpty(this.defaults);
this.inputs = [];
- this.enabled_filters = [];
-
- this.has_focus = false;
+ this.controls = {};
this.hidden = !!hidden;
this.headless = this.hidden && !this.has_defaults;
@@ -37,20 +102,63 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
this.ready = $.Deferred();
},
start: function() {
- this._super();
+ var self = this;
+ var p = this._super();
+
+ this.setup_global_completion();
+ this.vs = VS.init({
+ container: this.$element,
+ query: '',
+ callbacks: {
+ make_facet: this.proxy('make_visualsearch_facet'),
+ make_input: this.proxy('make_visualsearch_input'),
+ search: function (query, searchCollection) {
+ self.do_search();
+ },
+ facetMatches: function (callback) {
+ },
+ valueMatches : function(facet, searchTerm, callback) {
+ }
+ }
+ });
+
+ var search = function () { self.vs.searchBox.searchEvent({}); };
+ // searchQuery operations
+ this.vs.searchQuery
+ .off('add').on('add', search)
+ .off('change').on('change', search)
+ .off('reset').on('reset', search)
+ .off('remove').on('remove', function (record, collection, options) {
+ if (options['trigger_search']) {
+ search();
+ }
+ });
+
if (this.hidden) {
this.$element.hide();
}
if (this.headless) {
this.ready.resolve();
} else {
- this.rpc("/web/searchview/load", {
+ var load_view = this.rpc("/web/searchview/load", {
model: this.model,
view_id: this.view_id,
- context: this.dataset.get_context()
- }, this.on_loaded);
+ context: this.dataset.get_context() });
+ // FIXME: local eval of domain and context to get rid of special endpoint
+ var filters = this.rpc('/web/searchview/get_filters', {
+ model: this.model
+ }).then(function (filters) { self.custom_filters = filters; });
+
+ $.when(load_view, filters)
+ .pipe(function (load) { return load[0]; })
+ .then(this.on_loaded);
}
- return this.ready.promise();
+
+ this.$element.on('click', '.oe_vs_unfold_drawer', function () {
+ self.$element.toggleClass('oe_searchview_open_drawer');
+ });
+
+ return $.when(p, this.ready);
},
show: function () {
this.$element.show();
@@ -58,61 +166,227 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
hide: function () {
this.$element.hide();
},
+
+ /**
+ * Sets up thingie where all the mess is put?
+ */
+ setup_stuff_drawer: function () {
+ var self = this;
+ $('<div class="oe_vs_unfold_drawer">').appendTo(this.$element.find('.VS-search-box'));
+ var $drawer = $('<div class="oe_searchview_drawer">').appendTo(this.$element);
+ var $filters = $('<div class="oe_searchview_filters">').appendTo($drawer);
+
+ var running_count = 0;
+ // get total filters count
+ var is_group = function (i) { return i instanceof instance.web.search.FilterGroup; };
+ var filters_count = _(this.controls).chain()
+ .flatten()
+ .filter(is_group)
+ .map(function (i) { return i.filters.length; })
+ .sum()
+ .value();
+
+ var col1 = [], col2 = _(this.controls).map(function (inputs, group) {
+ var filters = _(inputs).filter(is_group);
+ return {
+ name: group === 'null' ? _t("Filters") : group,
+ filters: filters,
+ length: _(filters).chain().map(function (i) {
+ return i.filters.length; }).sum().value()
+ };
+ });
+
+ while (col2.length) {
+ // col1 + group should be smaller than col2 + group
+ if ((running_count + col2[0].length) <= (filters_count - running_count)) {
+ running_count += col2[0].length;
+ col1.push(col2.shift());
+ } else {
+ break;
+ }
+ }
+
+ // Create a Custom Filter FilterGroup for each custom filter read from
+ // the db, add all of this as a group in the smallest column
+ [].push.call(col1.length <= col2.length ? col1 : col2, {
+ name: _t("Custom Filters"),
+ filters: _.map(this.custom_filters, function (filter) {
+ // FIXME: handling of ``disabled`` being set
+ var f = new instance.web.search.Filter({attrs: {
+ string: filter.name,
+ context: filter.context,
+ domain: filter.domain
+ }}, self);
+ return new instance.web.search.FilterGroup([f], self);
+ }),
+ length: 3
+ });
+
+ return $.when(
+ this.render_column(col1, $('<div>').appendTo($filters)),
+ this.render_column(col2, $('<div>').appendTo($filters)),
+ (new instance.web.search.Advanced(this).appendTo($drawer)));
+ },
+ render_column: function (column, $el) {
+ return $.when.apply(null, _(column).map(function (group) {
+ $('<h3>').text(group.name).appendTo($el);
+ return $.when.apply(null,
+ _(group.filters).invoke('appendTo', $el));
+ }));
+ },
+ /**
+ * Sets up search view's view-wide auto-completion widget
+ */
+ setup_global_completion: function () {
+ // Prevent keydown from within a facet's input from reaching the
+ // auto-completion widget and opening the completion list
+ this.$element.on('keydown', '.search_facet input', function (e) {
+ e.stopImmediatePropagation();
+ });
+
+ this.$element.autocomplete({
+ source: this.proxy('complete_global_search'),
+ select: this.proxy('select_completion'),
+ focus: function (e) { e.preventDefault(); },
+ html: true,
+ minLength: 0,
+ delay: 0
+ }).data('autocomplete')._renderItem = function (ul, item) {
+ // item of completion list
+ var $item = $( "<li></li>" )
+ .data( "item.autocomplete", item )
+ .appendTo( ul );
+
+ if (item.value !== undefined) {
+ // regular completion item
+ return $item.append(
+ (item.label)
+ ? $('<a>').html(item.label)
+ : $('<a>').text(item.value));
+ }
+ return $item.text(item.category)
+ .css({
+ borderTop: '1px solid #cccccc',
+ margin: 0,
+ padding: 0,
+ zoom: 1,
+ 'float': 'left',
+ clear: 'left',
+ width: '100%'
+ });
+ }
+ },
+ /**
+ * Provide auto-completion result for req.term (an array to `resp`)
+ *
+ * @param {Object} req request to complete
+ * @param {String} req.term searched term to complete
+ * @param {Function} resp response callback
+ */
+ complete_global_search: function (req, resp) {
+ $.when.apply(null, _(this.inputs).chain()
+ .invoke('complete', req.term)
+ .value()).then(function () {
+ resp(_(_(arguments).compact()).flatten(true));
+ });
+ },
+
+ /**
+ * Action to perform in case of selection: create a facet (model)
+ * and add it to the search collection
+ *
+ * @param {Object} e selection event, preventDefault to avoid setting value on object
+ * @param {Object} ui selection information
+ * @param {Object} ui.item selected completion item
+ */
+ select_completion: function (e, ui) {
+ e.preventDefault();
+ this.vs.searchQuery.add(new VS.model.SearchFacet(_.extend(
+ {app: this.vs}, ui.item)));
+ this.vs.searchBox.searchEvent({});
+ },
+
+ /**
+ * Builds the right SearchFacet view based on the facet object to render
+ * (e.g. readonly facets for filters)
+ *
+ * @param {Object} options
+ * @param {VS.model.SearchFacet} options.model facet object to render
+ */
+ make_visualsearch_facet: function (options) {
+ return new instance.web.search.FilterGroupFacet(options);
+
+// if (options.model.get('field') instanceof instance.web.search.FilterGroup) {
+// return new instance.web.search.FilterGroupFacet(options);
+// }
+// return new VS.ui.SearchFacet(options);
+ },
+ /**
+ * Proxies searches on a SearchInput to the search view's global completion
+ *
+ * Also disables SearchInput.autocomplete#_move so search view's
+ * autocomplete can get the corresponding events, or something.
+ *
+ * @param options
+ */
+ make_visualsearch_input: function (options) {
+ var self = this, input = new VS.ui.SearchInput(options);
+ input.setupAutocomplete = function () {
+ _.extend(this.box.autocomplete({
+ minLength: 1,
+ delay: 0,
+ search: function () {
+ self.$element.autocomplete('search', input.box.val());
+ return false;
+ }
+ }).data('autocomplete'), {
+ _move: function () {},
+ close: function () { self.$element.autocomplete('close'); }
+ });
+ };
+ return input;
+ },
+
/**
* Builds a list of widget rows (each row is an array of widgets)
*
* @param {Array} items a list of nodes to convert to widgets
* @param {Object} fields a mapping of field names to (ORM) field attributes
- * @returns Array
+ * @param {String} [group_name] name of the group to put the new controls in
*/
- make_widgets: function (items, fields) {
- var rows = [],
- row = [];
- rows.push(row);
+ make_widgets: function (items, fields, group_name) {
+ group_name = group_name || null;
+ if (!(group_name in this.controls)) {
+ this.controls[group_name] = [];
+ }
+ var self = this, group = this.controls[group_name];
var filters = [];
_.each(items, function (item) {
if (filters.length && item.tag !== 'filter') {
- row.push(
- new openerp.web.search.FilterGroup(
- filters, this));
+ group.push(new instance.web.search.FilterGroup(filters, this));
filters = [];
}
- if (item.tag === 'newline') {
- row = [];
- rows.push(row);
- } else if (item.tag === 'filter') {
- if (!this.has_focus) {
- item.attrs.default_focus = '1';
- this.has_focus = true;
- }
- filters.push(
- new openerp.web.search.Filter(
- item, this));
- } else if (item.tag === 'separator') {
- // a separator is a no-op
- } else {
- if (item.tag === 'group') {
- // TODO: group and field should be fetched from registries, maybe even filters
- row.push(
- new openerp.web.search.Group(
- item, this, fields));
- } else if (item.tag === 'field') {
- if (!this.has_focus) {
- item.attrs.default_focus = '1';
- this.has_focus = true;
- }
- row.push(
- this.make_field(
- item, fields[item['attrs'].name]));
- }
+ switch (item.tag) {
+ case 'separator': case 'newline':
+ break;
+ case 'filter':
+ filters.push(new instance.web.search.Filter(item, this));
+ break;
+ case 'group':
+ self.make_widgets(item.children, fields, item.attrs.string);
+ break;
+ case 'field':
+ group.push(this.make_field(item, fields[item['attrs'].name]));
+ // filters
+ self.make_widgets(item.children, fields, group_name);
+ break;
}
}, this);
+
if (filters.length) {
- row.push(new openerp.web.search.FilterGroup(filters, this));
+ group.push(new instance.web.search.FilterGroup(filters, this));
}
-
- return rows;
},
/**
* Creates a field for the provided field descriptor item (which comes
@@ -120,18 +394,13 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
*
* @param {Object} item fields_view_get node for the field
* @param {Object} field fields_get result for the field
- * @returns openerp.web.search.Field
+ * @returns instance.web.search.Field
*/
make_field: function (item, field) {
- try {
- return new (openerp.web.search.fields.get_any(
- [item.attrs.widget, field.type]))
- (item, field, this);
- } catch (e) {
- if (! e instanceof openerp.web.KeyNotFound) {
- throw e;
- }
- // KeyNotFound means unknown field type
+ var obj = instance.web.search.fields.get_any( [item.attrs.widget, field.type]);
+ if(obj) {
+ return new (obj) (item, field, this);
+ } else {
console.group('Unknown field type ' + field.type);
console.error('View node', item);
console.info('View field', field);
@@ -141,6 +410,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
}
},
on_loaded: function(data) {
+ var self = this;
this.fields_view = data.fields_view;
if (data.fields_view.type !== 'search' ||
data.fields_view.arch.tag !== 'search') {
@@ -148,52 +418,20 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
"Got non-search view after asking for a search view: type %s, arch root %s",
data.fields_view.type, data.fields_view.arch.tag));
}
- var self = this,
- lines = this.make_widgets(
- data.fields_view['arch'].children,
- data.fields_view.fields);
-
- // for extended search view
- var ext = new openerp.web.search.ExtendedSearch(this, this.model);
- lines.push([ext]);
- this.extended_search = ext;
-
- var render = QWeb.render("SearchView", {
- 'view': data.fields_view['arch'],
- 'lines': lines,
- 'defaults': this.defaults
- });
- this.$element.html(render);
+ this.make_widgets(
+ data.fields_view['arch'].children,
+ data.fields_view.fields);
- var f = this.$element.find('form');
- this.$element.find('form')
- .submit(this.do_search)
- .bind('reset', this.do_clear);
- // start() all the widgets
- var widget_starts = _(lines).chain().flatten().map(function (widget) {
- return widget.start();
- }).value();
-
- $.when.apply(null, widget_starts).then(function () {
- self.ready.resolve();
- });
-
- this.reload_managed_filters();
- },
- reload_managed_filters: function() {
- var self = this;
- return this.rpc('/web/searchview/get_filters', {
- model: this.dataset.model
- }).then(function(result) {
- self.managed_filters = result;
- var filters = self.$element.find(".oe_search-view-filters-management");
- filters.html(QWeb.render("SearchView.managed-filters", {
- filters: result,
- disabled_filter_message: _t('Filter disabled due to invalid syntax')
- }));
- filters.change(self.on_filters_management);
- });
+ // load defaults
+ return $.when(
+ this.setup_stuff_drawer(),
+ $.when.apply(null, _(this.inputs).invoke('facet_for_defaults', this.defaults))
+ .then(function () {
+ self.vs.searchQuery.reset(_(arguments).compact(), {silent: true});
+ self.vs.searchBox.renderFacets();
+ }))
+ .then(function () { self.ready.resolve(); })
},
/**
* Handle event when the user make a selection in the filters management select box.
@@ -222,11 +460,11 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
break;
case 'save_filter':
var data = this.build_search_data();
- var context = new openerp.web.CompoundContext();
+ var context = new instance.web.CompoundContext();
_.each(data.contexts, function(x) {
context.add(x);
});
- var domain = new openerp.web.CompoundDomain();
+ var domain = new instance.web.CompoundDomain();
_.each(data.domains, function(x) {
domain.add(x);
});
@@ -234,7 +472,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
context.add({"group_by": groupbys});
var dial_html = QWeb.render("SearchView.managed-filters.add");
var $dial = $(dial_html);
- openerp.web.dialog($dial, {
+ instance.web.dialog($dial, {
modal: true,
title: _t("Filter Entry"),
buttons: [
@@ -287,13 +525,13 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
on_add_to_dashboard: function() {
this.$element.find(".oe_search-view-filters-management")[0].selectedIndex = 0;
var self = this,
- menu = openerp.webclient.menu,
+ menu = instance.webclient.menu,
$dialog = $(QWeb.render("SearchView.add_to_dashboard", {
dashboards : menu.data.data.children,
selected_menu_id : menu.$element.find('a.active').data('menu')
}));
$dialog.find('input').val(this.fields_view.name);
- openerp.web.dialog($dialog, {
+ instance.web.dialog($dialog, {
modal: true,
title: _t("Add to Dashboard"),
buttons: [
@@ -305,8 +543,8 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
var menu_id = $(this).find("select").val(),
title = $(this).find("input").val(),
data = self.build_search_data(),
- context = new openerp.web.CompoundContext(),
- domain = new openerp.web.CompoundDomain();
+ context = new instance.web.CompoundContext(),
+ domain = new instance.web.CompoundDomain();
_.each(data.contexts, function(x) {
context.add(x);
});
@@ -335,47 +573,33 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
* Performs the search view collection of widget data.
*
* If the collection went well (all fields are valid), then triggers
- * :js:func:`openerp.web.SearchView.on_search`.
+ * :js:func:`instance.web.SearchView.on_search`.
*
* If at least one field failed its validation, triggers
- * :js:func:`openerp.web.SearchView.on_invalid` instead.
+ * :js:func:`instance.web.SearchView.on_invalid` instead.
*
* @param e jQuery event object coming from the "Search" button
*/
- do_search: function (e) {
- if (this.headless && !this.has_defaults) {
- return this.on_search([], [], []);
- }
-
- if (e && e.preventDefault) { e.preventDefault(); }
-
- var data = this.build_search_data();
+ do_search: function () {
+ var domains = [], contexts = [], groupbys = [], errors = [];
- if (data.errors.length) {
- this.on_invalid(data.errors);
- return;
- }
-
- this.on_search(data.domains, data.contexts, data.groupbys);
- },
- build_search_data: function() {
- var domains = [],
- contexts = [],
- errors = [];
-
- _.each(this.inputs, function (input) {
+ this.vs.searchQuery.each(function (facet) {
+ var field = facet.get('field');
try {
- var domain = input.get_domain();
+ var domain = field.get_domain(facet);
if (domain) {
domains.push(domain);
}
-
- var context = input.get_context();
+ var context = field.get_context(facet);
if (context) {
contexts.push(context);
}
+ var group_by = field.get_groupby(facet);
+ if (group_by) {
+ groupbys.push.apply(groupbys, group_by);
+ }
} catch (e) {
- if (e instanceof openerp.web.search.Invalid) {
+ if (e instanceof instance.web.search.Invalid) {
errors.push(e);
} else {
throw e;
@@ -383,23 +607,11 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
}
});
- // TODO: do we need to handle *fields* with group_by in their context?
- var groupbys = _(this.enabled_filters)
- .chain()
- .map(function (filter) { return filter.get_context();})
- .compact()
- .value();
-
- if (this.filter_data.contexts) {
- contexts = this.filter_data.contexts.concat(contexts)
- }
- if (this.filter_data.domains) {
- domains = this.filter_data.domains.concat(domains);
- }
- if (this.filter_data.groupbys) {
- groupbys = this.filter_data.groupbys.concat(groupbys);
+ if (!_.isEmpty(errors)) {
+ this.on_invalid(errors);
+ return;
}
- return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
+ return this.on_search(domains, contexts, groupbys);
},
/**
* Triggered after the SearchView has collected all relevant domains and
@@ -432,84 +644,68 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
*/
on_invalid: function (errors) {
this.do_notify(_t("Invalid Search"), _t("triggered from search view"));
- },
- /**
- * @param {Boolean} [reload_view=true]
- */
- do_clear: function (reload_view) {
- this.filter_data = {};
- this.$element.find(".oe_search-view-filters-management").val('');
+ }
+});
+
+/** @namespace */
+instance.web.search = {};
- this.$element.find('.filter_label, .filter_icon').removeClass('enabled');
- this.enabled_filters.splice(0);
- var string = $('a.searchview_group_string');
- _.each(string, function(str){
- $(str).closest('div.searchview_group').removeClass("expanded").addClass('folded');
- });
- this.$element.find('table:last').hide();
+instance.web.search.FilterGroupFacet = VS.ui.SearchFacet.extend({
+ events: _.extend({
+ 'click': 'selectFacet'
+ }, VS.ui.SearchFacet.prototype.events),
- $('.searchview_extended_groups_list').empty();
- return $.async_when.apply(
- null, _(this.inputs).invoke('clear')).pipe(
- reload_view !== false ? this.on_clear : null);
+ render: function () {
+ this.setMode('not', 'editing');
+ this.setMode('not', 'selected');
+
+ var value = this.model.get('value');
+ this.$el.html(QWeb.render('SearchView.filters.facet', {
+ facet: this.model
+ }));
+ // virtual input so SearchFacet code has something to play with
+ this.box = $('<input>').val(value);
+
+ return this;
},
- /**
- * Triggered when the search view gets cleared
- *
- * @event
- */
- on_clear: function () {
- this.do_search();
+ enableEdit: function () {
+ this.selectFacet()
},
- /**
- * Called by a filter propagating its state changes
- *
- * @param {openerp.web.search.Filter} filter a filter which got toggled
- * @param {Boolean} default_enabled filter got enabled through the default values, at render time.
- */
- do_toggle_filter: function (filter, default_enabled) {
- if (default_enabled || filter.is_enabled()) {
- this.enabled_filters.push(filter);
- } else {
- this.enabled_filters = _.without(
- this.enabled_filters, filter);
- }
-
- if (!default_enabled) {
- // selecting a filter after initial loading automatically
- // triggers refresh
- this.$element.find('form').submit();
+ keydown: function (e) {
+ var key = VS.app.hotkeys.key(e);
+ if (key !== 'right') {
+ return VS.ui.SearchFacet.prototype.keydown.call(this, e);
}
+ e.preventDefault();
+ this.deselectFacet();
+ this.options.app.searchBox.focusNextFacet(this, 1);
}
});
-
-/** @namespace */
-openerp.web.search = {};
/**
- * Registry of search fields, called by :js:class:`openerp.web.SearchView` to
+ * Registry of search fields, called by :js:class:`instance.web.SearchView` to
* find and instantiate its field widgets.
*/
-openerp.web.search.fields = new openerp.web.Registry({
- 'char': 'openerp.web.search.CharField',
- 'text': 'openerp.web.search.CharField',
- 'boolean': 'openerp.web.search.BooleanField',
- 'integer': 'openerp.web.search.IntegerField',
- 'id': 'openerp.web.search.IntegerField',
- 'float': 'openerp.web.search.FloatField',
- 'selection': 'openerp.web.search.SelectionField',
- 'datetime': 'openerp.web.search.DateTimeField',
- 'date': 'openerp.web.search.DateField',
- 'many2one': 'openerp.web.search.ManyToOneField',
- 'many2many': 'openerp.web.search.CharField',
- 'one2many': 'openerp.web.search.CharField'
+instance.web.search.fields = new instance.web.Registry({
+ 'char': 'instance.web.search.CharField',
+ 'text': 'instance.web.search.CharField',
+ 'boolean': 'instance.web.search.BooleanField',
+ 'integer': 'instance.web.search.IntegerField',
+ 'id': 'instance.web.search.IntegerField',
+ 'float': 'instance.web.search.FloatField',
+ 'selection': 'instance.web.search.SelectionField',
+ 'datetime': 'instance.web.search.DateTimeField',
+ 'date': 'instance.web.search.DateField',
+ 'many2one': 'instance.web.search.ManyToOneField',
+ 'many2many': 'instance.web.search.CharField',
+ 'one2many': 'instance.web.search.CharField'
});
-openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.search.Invalid# */{
+instance.web.search.Invalid = instance.web.Class.extend( /** @lends instance.web.search.Invalid# */{
/**
* Exception thrown by search widgets when they hold invalid values,
* which they can not return when asked.
*
- * @constructs openerp.web.search.Invalid
- * @extends openerp.web.Class
+ * @constructs instance.web.search.Invalid
+ * @extends instance.web.Class
*
* @param field the name of the field holding an invalid value
* @param value the invalid value
@@ -527,92 +723,42 @@ openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.se
);
}
});
-openerp.web.search.Widget = openerp.web.OldWidget.extend( /** @lends openerp.web.search.Widget# */{
+instance.web.search.Widget = instance.web.OldWidget.extend( /** @lends instance.web.search.Widget# */{
template: null,
/**
* Root class of all search widgets
*
- * @constructs openerp.web.search.Widget
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.search.Widget
+ * @extends instance.web.OldWidget
*
* @param view the ancestor view of this widget
*/
init: function (view) {
this._super(view);
this.view = view;
- },
- /**
- * Sets and returns a globally unique identifier for the widget.
- *
- * If a prefix is specified, the identifier will be appended to it.
- *
- * @params prefix prefix sections, empty/falsy sections will be removed
- */
- make_id: function () {
- this.element_id = _.uniqueId(
- ['search'].concat(
- _.compact(_.toArray(arguments)),
- ['']).join('_'));
- return this.element_id;
- },
- /**
- * "Starts" the widgets. Called at the end of the rendering, this allows
- * widgets to hook themselves to their view sections.
- *
- * On widgets, if they kept a reference to a view and have an element_id,
- * will fetch and set their root element on $element.
- */
- start: function () {
- this._super();
- if (this.view && this.element_id) {
- // id is unique, and no getElementById on elements
- this.$element = $(document.getElementById(
- this.element_id));
- }
- },
- /**
- * "Stops" the widgets. Called when the view destroys itself, this
- * lets the widgets clean up after themselves.
- */
- destroy: function () {
- delete this.view;
- this._super();
- },
- render: function (defaults) {
- // FIXME
- return this._super(_.extend(this, {defaults: defaults}));
}
});
-openerp.web.search.add_expand_listener = function($root) {
+instance.web.search.add_expand_listener = function($root) {
$root.find('a.searchview_group_string').click(function (e) {
$root.toggleClass('folded expanded');
e.stopPropagation();
e.preventDefault();
});
};
-openerp.web.search.Group = openerp.web.search.Widget.extend({
+instance.web.search.Group = instance.web.search.Widget.extend({
template: 'SearchView.group',
init: function (view_section, view, fields) {
this._super(view);
this.attrs = view_section.attrs;
this.lines = view.make_widgets(
view_section.children, fields);
- this.make_id('group');
- },
- start: function () {
- this._super();
- openerp.web.search.add_expand_listener(this.$element);
- var widget_starts = _(this.lines).chain().flatten()
- .map(function (widget) { return widget.start(); })
- .value();
- return $.when.apply(null, widget_starts);
}
});
-openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.web.search.Input# */{
+instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instance.web.search.Input# */{
/**
- * @constructs openerp.web.search.Input
- * @extends openerp.web.search.Widget
+ * @constructs instance.web.search.Input
+ * @extends instance.web.search.Widget
*
* @param view
*/
@@ -621,10 +767,44 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.
this.view.inputs.push(this);
this.style = undefined;
},
+ /**
+ * Fetch auto-completion values for the widget.
+ *
+ * The completion values should be an array of objects with keys category,
+ * label, value prefixed with an object with keys type=section and label
+ *
+ * @param {String} value value to complete
+ * @returns {jQuery.Deferred<null|Array>}
+ */
+ complete: function (value) {
+ return $.when(null)
+ },
+ /**
+ * Returns a VS.model.SearchFacet instance for the provided defaults if
+ * they apply to this widget, or null if they don't.
+ *
+ * This default implementation will try calling
+ * :js:func:`instance.web.search.Input#facet_for` if the widget's name
+ * matches the input key
+ *
+ * @param {Object} defaults
+ * @returns {jQuery.Deferred<null|Object>}
+ */
+ facet_for_defaults: function (defaults) {
+ if (!this.attrs ||
+ !(this.attrs.name in defaults && defaults[this.attrs.name])) {
+ return $.when(null);
+ }
+ return this.facet_for(defaults[this.attrs.name]);
+ },
get_context: function () {
throw new Error(
"get_context not implemented for widget " + this.attrs.type);
},
+ get_groupby: function () {
+ throw new Error(
+ "get_groupby not implemented for widget " + this.attrs.type);
+ },
get_domain: function () {
throw new Error(
"get_domain not implemented for widget " + this.attrs.type);
@@ -638,42 +818,80 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.
}
}
this.attrs = attrs;
- },
- /**
- * Specific clearing operations, if any
- */
- clear: function () {}
+ }
});
-openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends openerp.web.search.FilterGroup# */{
+instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends instance.web.search.FilterGroup# */{
template: 'SearchView.filters',
/**
* Inclusive group of filters, creates a continuous "button" with clickable
* sections (the normal display for filters is to be a self-contained button)
*
- * @constructs openerp.web.search.FilterGroup
- * @extends openerp.web.search.Input
+ * @constructs instance.web.search.FilterGroup
+ * @extends instance.web.search.Input
*
- * @param {Array<openerp.web.search.Filter>} filters elements of the group
- * @param {openerp.web.SearchView} view view in which the filters are contained
+ * @param {Array<instance.web.search.Filter>} filters elements of the group
+ * @param {instance.web.SearchView} view view in which the filters are contained
*/
init: function (filters, view) {
this._super(view);
this.filters = filters;
- this.length = filters.length;
},
start: function () {
- this._super();
- _.each(this.filters, function (filter) {
- filter.start();
+ this.$element.on('click', 'li', this.proxy('toggle_filter'));
+ return $.when(null);
+ },
+ facet_for_defaults: function (defaults) {
+ var fs = _(this.filters).filter(function (f) {
+ return f.attrs && f.attrs.name && !!defaults[f.attrs.name];
});
+ if (_.isEmpty(fs)) { return $.when(null); }
+ return $.when(new VS.model.SearchFacet({
+ category: _t("Filter"),
+ value: _(fs).map(function (f) {
+ return f.attrs.string || f.attrs.name }).join(' | '),
+ json: fs,
+ field: this,
+ app: this.view.vs
+ }));
+ },
+ /**
+ * Fetches contexts for all enabled filters in the group
+ *
+ * @param {VS.model.SearchFacet} facet
+ * @return {*} combined contexts of the enabled filters in this group
+ */
+ get_context: function (facet) {
+ var contexts = _(facet.get('json')).chain()
+ .map(function (filter) { return filter.attrs.context; })
+ .reject(_.isEmpty)
+ .value();
+
+ if (!contexts.length) { return; }
+ if (contexts.length === 1) { return contexts[0]; }
+ return _.extend(new instance.web.CompoundContext, {
+ __contexts: contexts
+ });
+ },
+ /**
+ * Fetches group_by sequence for all enabled filters in the group
+ *
+ * @param {VS.model.SearchFacet} facet
+ * @return {Array} enabled filters in this group
+ */
+ get_groupby: function (facet) {
+ return _(facet.get('json')).chain()
+ .map(function (filter) { return filter.attrs.context; })
+ .reject(_.isEmpty)
+ .value();
},
- get_context: function () { },
/**
* Handles domains-fetching for all the filters within it: groups them.
+ *
+ * @param {VS.model.SearchFacet} facet
+ * @return {*} combined domains of the enabled filters in this group
*/
- get_domain: function () {
- var domains = _(this.filters).chain()
- .filter(function (filter) { return filter.is_enabled(); })
+ get_domain: function (facet) {
+ var domains = _(facet.get('json')).chain()
.map(function (filter) { return filter.attrs.domain; })
.reject(_.isEmpty)
.value();
@@ -683,19 +901,62 @@ openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends open
for (var i=domains.length; --i;) {
domains.unshift(['|']);
}
- return _.extend(new openerp.web.CompoundDomain(), {
+ return _.extend(new instance.web.CompoundDomain(), {
__domains: domains
});
+ },
+ toggle_filter: function (e) {
+ this.toggle(this.filters[$(e.target).index()]);
+ },
+ toggle: function (filter) {
+ // FIXME: oh god, my eyes, they hurt
+ var self = this, fs;
+ var facet = this.view.vs.searchQuery.detect(function (f) {
+ return f.get('field') === self; });
+ if (facet) {
+ fs = facet.get('json');
+
+ if (_.include(fs, filter)) {
+ fs = _.without(fs, filter);
+ } else {
+ fs.push(filter);
+ }
+ if (_(fs).isEmpty()) {
+ this.view.vs.searchQuery.remove(facet, {trigger_search: true});
+ } else {
+ facet.set({
+ json: fs,
+ value: _(fs).map(function (f) {
+ return f.attrs.string || f.attrs.name }).join(' | ')
+ });
+ }
+ return;
+ } else {
+ fs = [filter];
+ }
+
+ this.view.vs.searchQuery.add({
+ category: _t("Filter"),
+ value: _(fs).map(function (f) {
+ return f.attrs.string || f.attrs.name }).join(' | '),
+ json: fs,
+ field: this,
+ app: this.view.vs
+ });
}
});
-openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.web.search.Filter# */{
+instance.web.search.Filter = instance.web.search.Input.extend(/** @lends instance.web.search.Filter# */{
template: 'SearchView.filter',
/**
* Implementation of the OpenERP filters (button with a context and/or
* a domain sent as-is to the search view)
*
- * @constructs openerp.web.search.Filter
- * @extends openerp.web.search.Input
+ * Filters are only attributes holder, the actual work (compositing
+ * domains and contexts, converting between facets and filters) is
+ * performed by the filter group.
+ *
+ * @constructs instance.web.search.Filter
+ * @extends instance.web.search.Input
*
* @param node
* @param view
@@ -703,56 +964,17 @@ openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.w
init: function (node, view) {
this._super(view);
this.load_attrs(node.attrs);
- this.classes = [this.attrs.string ? 'filter_label' : 'filter_icon'];
- this.make_id('filter', this.attrs.name);
- },
- start: function () {
- this._super();
- var self = this;
- this.$element.click(function (e) {
- $(this).toggleClass('enabled');
- self.view.do_toggle_filter(self);
- });
- },
- /**
- * Returns whether the filter is currently enabled (in use) or not.
- *
- * @returns a boolean
- */
- is_enabled:function () {
- return this.$element.hasClass('enabled');
- },
- /**
- * If the filter is present in the defaults (and has a truthy value),
- * enable the filter.
- *
- * @param {Object} defaults the search view's default values
- */
- render: function (defaults) {
- if (this.attrs.name && defaults[this.attrs.name]) {
- this.classes.push('enabled');
- this.view.do_toggle_filter(this, true);
- }
- return this._super(defaults);
},
- get_context: function () {
- if (!this.is_enabled()) {
- return;
- }
- return this.attrs.context;
- },
- /**
- * Does not return anything: filter domain is handled at the FilterGroup
- * level
- */
- get_domain: function () { }
+ facet_for: function () { return $.when(null); },
+ get_context: function () { },
+ get_domain: function () { },
});
-openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.web.search.Field# */ {
+instance.web.search.Field = instance.web.search.Input.extend( /** @lends instance.web.search.Field# */ {
template: 'SearchView.field',
default_operator: '=',
/**
- * @constructs openerp.web.search.Field
- * @extends openerp.web.search.Input
+ * @constructs instance.web.search.Field
+ * @extends instance.web.search.Input
*
* @param view_section
* @param field
@@ -761,25 +983,21 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
init: function (view_section, field, view) {
this._super(view);
this.load_attrs(_.extend({}, field, view_section.attrs));
- this.filters = new openerp.web.search.FilterGroup(_.compact(_.map(
- view_section.children, function (filter_node) {
- if (filter_node.attrs.string &&
- typeof console !== 'undefined' && console.debug) {
- console.debug("Filter-in-field with a 'string' attribute "
- + "in view", view);
- }
- delete filter_node.attrs.string;
- return new openerp.web.search.Filter(
- filter_node, view);
- })), view);
- this.make_id('input', field.type, this.attrs.name);
},
- start: function () {
- this._super();
- this.filters.start();
+ facet_for: function (value) {
+ return $.when(new VS.model.SearchFacet({
+ category: this.attrs.string || this.attrs.name,
+ value: String(value),
+ json: value,
+ field: this,
+ app: this.view.vs
+ }));
},
- get_context: function () {
- var val = this.get_value();
+ get_value: function (facet) {
+ return facet.value();
+ },
+ get_context: function (facet) {
+ var val = this.get_value(facet);
// A field needs a value to be "active", and a context to send when
// active
var has_value = (val !== null && val !== '');
@@ -787,10 +1005,10 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
if (!(has_value && context)) {
return;
}
- return _.extend(
- {}, context,
- {own_values: {self: val}});
+ return new instance.web.CompoundContext(context)
+ .set_eval_context({self: val});
},
+ get_groupby: function () { },
/**
* Function creating the returned domain for the field, override this
* methods in children if you only need to customize the field's domain
@@ -802,11 +1020,11 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
* @param {Number|String} value parsed value for the field
* @returns {Array<Array>} domain to include in the resulting search
*/
- make_domain: function (name, operator, value) {
- return [[name, operator, value]];
+ make_domain: function (name, operator, facet) {
+ return [[name, operator, this.get_value(facet)]];
},
- get_domain: function () {
- var val = this.get_value();
+ get_domain: function (facet) {
+ var val = this.get_value(facet);
if (val === null || val === '') {
return;
}
@@ -816,9 +1034,10 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
return this.make_domain(
this.attrs.name,
this.attrs.operator || this.default_operator,
- val);
+ facet);
}
- return _.extend({}, domain, {own_values: {self: val}});
+ return new instance.web.CompoundDomain(domain)
+ .set_eval_context({self: val});
}
});
/**
@@ -829,15 +1048,25 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
* * The Javascript and the HTML values are identical (strings)
*
* @class
- * @extends openerp.web.search.Field
+ * @extends instance.web.search.Field
*/
-openerp.web.search.CharField = openerp.web.search.Field.extend( /** @lends openerp.web.search.CharField# */ {
+instance.web.search.CharField = instance.web.search.Field.extend( /** @lends instance.web.search.CharField# */ {
default_operator: 'ilike',
- get_value: function () {
- return this.$element.val();
+ complete: function (value) {
+ if (_.isEmpty(value)) { return $.when(null); }
+ var label = _.str.sprintf(_.str.escapeHTML(
+ _t("Search %(field)s for: %(value)s")), {
+ field: '<em>' + this.attrs.string + '</em>',
+ value: '<strong>' + _.str.escapeHTML(value) + '</strong>'});
+ return $.when([{
+ category: this.attrs.string,
+ label: label,
+ value: value,
+ field: this
+ }]);
}
});
-openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends openerp.web.search.NumberField# */{
+instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{
get_value: function () {
if (!this.$element.val()) {
return null;
@@ -846,7 +1075,7 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open
check = Number(this.$element.val());
if (isNaN(val) || val !== check) {
this.$element.addClass('error');
- throw new openerp.web.search.Invalid(
+ throw new instance.web.search.Invalid(
this.attrs.name, this.$element.val(), this.error_message);
}
this.$element.removeClass('error');
@@ -855,13 +1084,13 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open
});
/**
* @class
- * @extends openerp.web.search.NumberField
+ * @extends instance.web.search.NumberField
*/
-openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.IntegerField# */{
+instance.web.search.IntegerField = instance.web.search.NumberField.extend(/** @lends instance.web.search.IntegerField# */{
error_message: _t("not a valid integer"),
parse: function (value) {
try {
- return openerp.web.parse_value(value, {'widget': 'integer'});
+ return instance.web.parse_value(value, {'widget': 'integer'});
} catch (e) {
return NaN;
}
@@ -869,13 +1098,13 @@ openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @len
});
/**
* @class
- * @extends openerp.web.search.NumberField
+ * @extends instance.web.search.NumberField
*/
-openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.FloatField# */{
+instance.web.search.FloatField = instance.web.search.NumberField.extend(/** @lends instance.web.search.FloatField# */{
error_message: _t("not a valid number"),
parse: function (value) {
try {
- return openerp.web.parse_value(value, {'widget': 'float'});
+ return instance.web.parse_value(value, {'widget': 'float'});
} catch (e) {
return NaN;
}
@@ -883,9 +1112,9 @@ openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends
});
/**
* @class
- * @extends openerp.web.search.Field
+ * @extends instance.web.search.Field
*/
-openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends openerp.web.search.SelectionField# */{
+instance.web.search.SelectionField = instance.web.search.Field.extend(/** @lends instance.web.search.SelectionField# */{
// This implementation is a basic <select> field, but it may have to be
// altered to be more in line with the GTK client, which uses a combo box
// (~ jquery.autocomplete):
@@ -904,48 +1133,48 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o
return !item[1];
});
},
- get_value: function () {
- var index = parseInt(this.$element.val(), 10);
- if (isNaN(index)) { return null; }
- var value = this.attrs.selection[index][0];
- if (value === false) { return null; }
- return value;
- },
- /**
- * The selection field needs a default ``false`` value in case none is
- * provided, so that selector options with a ``false`` value (convention
- * for explicitly empty options) get selected by default rather than the
- * first (value-holding) option in the selection.
- *
- * @param {Object} defaults search default values
- */
- render: function (defaults) {
- if (!defaults[this.attrs.name]) {
- defaults[this.attrs.name] = false;
- }
- return this._super(defaults);
- },
- clear: function () {
- var self = this, d = $.Deferred(), selection = this.attrs.selection;
- for(var index=0; index<selection.length; ++index) {
- var item = selection[index];
- if (!item[1]) {
- setTimeout(function () {
- // won't override mutable, because we immediately bail out
- //noinspection JSReferencingMutableVariableFromClosure
- self.$element.val(index);
- d.resolve();
- }, 0);
- return d.promise();
- }
- }
- return d.resolve().promise();
+ complete: function (needle) {
+ var self = this;
+ var results = _(this.attrs.selection).chain()
+ .filter(function (sel) {
+ var value = sel[0], label = sel[1];
+ if (!value) { return false; }
+ return label.toLowerCase().indexOf(needle.toLowerCase()) !== -1;
+ })
+ .map(function (sel) {
+ return {
+ category: self.attrs.string,
+ field: self,
+ value: sel[1],
+ json: sel[0]
+ };
+ }).value();
+ if (_.isEmpty(results)) { return $.when(null); }
+ return $.when.apply(null, [{
+ category: this.attrs.string
+ }].concat(results));
+ },
+ facet_for: function (value) {
+ var match = _(this.attrs.selection).detect(function (sel) {
+ return sel[0] === value;
+ });
+ if (!match) { return $.when(null); }
+ return $.when(new VS.model.SearchFacet({
+ category: this.attrs.string,
+ value: match[1],
+ json: match[0],
+ field: this,
+ app: this.view.app
+ }));
+ },
+ get_value: function (facet) {
+ return facet.get('json');
}
});
-openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @lends openerp.web.search.BooleanField# */{
+instance.web.search.BooleanField = instance.web.search.SelectionField.extend(/** @lends instance.web.search.BooleanField# */{
/**
- * @constructs openerp.web.search.BooleanField
- * @extends openerp.web.search.BooleanField
+ * @constructs instance.web.search.BooleanField
+ * @extends instance.web.search.BooleanField
*/
init: function () {
this._super.apply(this, arguments);
@@ -954,25 +1183,8 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
['false', _t("No")]
];
},
- /**
- * Search defaults likely to be boolean values (for a boolean field).
- *
- * In the HTML, we only want/get strings, and our strings here are ``true``
- * and ``false``, so ensure we use precisely those by truth-testing the
- * default value (iif there is one in the view's defaults).
- *
- * @param {Object} defaults default values for this search view
- * @returns {String} rendered boolean field
- */
- render: function (defaults) {
- var name = this.attrs.name;
- if (name in defaults) {
- defaults[name] = defaults[name] ? "true" : "false";
- }
- return this._super(defaults);
- },
- get_value: function () {
- switch (this._super()) {
+ get_value: function (facet) {
+ switch (this._super(facet)) {
case 'false': return false;
case 'true': return true;
default: return null;
@@ -981,26 +1193,27 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
});
/**
* @class
- * @extends openerp.web.search.DateField
+ * @extends instance.web.search.DateField
*/
-openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{
- template: "SearchView.date",
- start: function () {
- this._super();
- // FIXME: this insanity puts a div inside a span
- this.datewidget = new openerp.web.DateWidget(this);
- this.datewidget.prependTo(this.$element);
- this.datewidget.$element.find("input")
- .attr("size", 15)
- .attr("autofocus", this.attrs.default_focus === '1' ? 'autofocus' : null)
- .removeAttr('style');
- this.datewidget.set_value(this.defaults[this.attrs.name] || false);
- },
- get_value: function () {
- return this.datewidget.get_value() || null;
- },
- clear: function () {
- this.datewidget.set_value(false);
+instance.web.search.DateField = instance.web.search.Field.extend(/** @lends instance.web.search.DateField# */{
+ get_value: function (facet) {
+ return instance.web.date_to_str(facet.get('json'));
+ },
+ complete: function (needle) {
+ var d = Date.parse(needle);
+ if (!d) { return $.when(null); }
+ var value = instance.web.format_value(d, this.attrs);
+ var label = _.str.sprintf(_.str.escapeHTML(
+ _t("Search %(field)s at: %(value)s")), {
+ field: '<em>' + this.attrs.string + '</em>',
+ value: '<strong>' + value + '</strong>'});
+ return $.when([{
+ category: this.attrs.string,
+ label: label,
+ value: value,
+ json: d,
+ field: this
+ }]);
}
});
/**
@@ -1012,205 +1225,135 @@ openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends opener
* spanning the whole day selected by the date widget
*
* @class
- * @extends openerp.web.DateField
+ * @extends instance.web.DateField
*/
-openerp.web.search.DateTimeField = openerp.web.search.DateField.extend(/** @lends openerp.web.search.DateTimeField# */{
- make_domain: function (name, operator, value) {
- return ['&', [name, '>=', value + ' 00:00:00'],
- [name, '<=', value + ' 23:59:59']];
+instance.web.search.DateTimeField = instance.web.search.DateField.extend(/** @lends instance.web.search.DateTimeField# */{
+ get_value: function (facet) {
+ return instance.web.datetime_to_str(facet.get('json'));
}
});
-openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({
+instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
init: function (view_section, field, view) {
this._super(view_section, field, view);
+ this.model = new instance.web.Model(this.attrs.relation);
+ },
+ complete: function (needle) {
var self = this;
- this.got_name = $.Deferred().then(function () {
- self.$element.val(self.name);
+ // TODO: context
+ // FIXME: "concurrent" searches (multiple requests, mis-ordered responses)
+ return this.model.call('name_search', [], {
+ name: needle,
+ limit: 8,
+ context: {}
+ }).pipe(function (results) {
+ if (_.isEmpty(results)) { return null; }
+ return [{category: self.attrs.string}].concat(
+ _(results).map(function (result) {
+ return {
+ category: self.attrs.string,
+ value: result[1],
+ json: result[0],
+ field: self
+ };
+ }));
});
- this.model = new openerp.web.Model(this.attrs['relation']);
-
- this.orderer = new openerp.web.DropMisordered();
- },
- start: function () {
- this._super();
- this.setup_autocomplete();
- var started = $.Deferred();
- this.got_name.then(function () { started.resolve();},
- function () { started.resolve(); });
- return started.promise();
},
- setup_autocomplete: function () {
+ facet_for: function (value) {
var self = this;
- this.$element.autocomplete({
- source: function (req, resp) {
- self.orderer.add(self.model.call('name_search', null, {
- name: req.term,
- args: self.attrs.domain,
- limit: 8
- })).then(function (data) {
- resp(_.map(data, function (result) {
- return {id: result[0], label: result[1]}
- }));
- });
- },
- select: function (event, ui) {
- self.id = ui.item.id;
- self.name = ui.item.label;
- },
- delay: 100
- })
- },
- on_name_get: function (name_get) {
- if (!name_get.length) {
- delete this.id;
- this.got_name.reject();
- return;
- }
- this.name = name_get[0][1];
- this.got_name.resolve();
- },
- render: function (defaults) {
- if (defaults[this.attrs.name]) {
- this.id = defaults[this.attrs.name];
- if (this.id instanceof Array)
- this.id = this.id[0];
- // TODO: maybe this should not be completely removed
- delete defaults[this.attrs.name];
- this.model.call('name_get', [[this.id]]).then(
- this.proxy('on_name_get'));
- } else {
- this.got_name.reject();
+ if (value instanceof Array) {
+ return $.when(new VS.model.SearchFacet({
+ category: this.attrs.string,
+ value: value[1],
+ json: value[0],
+ field: this,
+ app: this.view.vs
+ }));
}
- return this._super(defaults);
+ return this.model.call('name_get', [value], {}).pipe(function (names) {
+ return new VS.model.SearchFacet({
+ category: self.attrs.string,
+ value: names[0][1],
+ json: names[0][0],
+ field: self,
+ app: self.view.vs
+ });
+ })
},
- make_domain: function (name, operator, value) {
- if (this.id && this.name) {
- if (value === this.name) {
- return [[name, '=', this.id]];
- } else {
- delete this.id;
- delete this.name;
- }
+ make_domain: function (name, operator, facet) {
+ // ``json`` -> actual auto-completed id
+ if (facet.get('json')) {
+ return [[name, '=', facet.get('json')]];
}
- return this._super(name, operator, value);
+
+ return this._super(name, operator, facet);
}
});
-openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
- template: 'SearchView.extended_search',
- init: function (parent, model) {
- this._super(parent);
- this.model = model;
- },
- add_group: function() {
- var group = new openerp.web.search.ExtendedSearchGroup(this, this.fields);
- group.appendTo(this.$element.find('.searchview_extended_groups_list'));
- this.check_last_element();
- },
+instance.web.search.Advanced = instance.web.search.Input.extend({
+ template: 'SearchView.advanced',
start: function () {
- this._super();
- this.$element.closest("table.oe-searchview-render-line").css("display", "none");
var self = this;
- this.rpc("/web/searchview/fields_get",
- {"model": this.model}, function(data) {
- self.fields = data.fields;
- if (!('id' in self.fields)) {
- self.fields.id = {
- string: 'ID',
- type: 'id'
- }
- }
- openerp.web.search.add_expand_listener(self.$element);
- self.$element.find('.searchview_extended_add_group').click(function (e) {
- self.add_group();
+ this.$element
+ .on('keypress keydown keyup', function (e) { e.stopPropagation(); })
+ .on('click', 'h4', function () {
+ self.$element.toggleClass('oe_opened');
+ }).on('click', 'button.oe_add_condition', function () {
+ self.append_proposition();
+ }).on('submit', 'form', function (e) {
+ e.preventDefault();
+ self.commit_search();
});
+ return $.when(
+ this._super(),
+ this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) {
+ self.fields = _.extend({
+ id: { string: 'ID', type: 'id' }
+ }, data.fields);
+ })).then(function () {
+ self.append_proposition();
});
},
- get_context: function() {
- return null;
- },
- get_domain: function() {
- if (!this.$element) {
- return null; // not a logical state but sometimes it happens
- }
- if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
- return null;
- }
- return _.reduce(this.getChildren(),
- function(mem, x) { return mem.concat(x.get_domain());}, []);
- },
- on_activate: function() {
- this.add_group();
- var table = this.$element.closest("table.oe-searchview-render-line");
- table.css("display", "");
- if(this.$element.hasClass("folded")) {
- this.$element.toggleClass("folded expanded");
- }
- },
- hide: function() {
- var table = this.$element.closest("table.oe-searchview-render-line");
- table.css("display", "none");
- if(this.$element.hasClass("expanded")) {
- this.$element.toggleClass("folded expanded");
- }
- },
- check_last_element: function() {
- _.each(this.getChildren(), function(x) {x.set_last_group(false);});
- if (this.getChildren().length >= 1) {
- this.getChildren()[this.getChildren().length - 1].set_last_group(true);
- }
- }
-});
-
-openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
- template: 'SearchView.extended_search.group',
- init: function (parent, fields) {
- this._super(parent);
- this.fields = fields;
- },
- add_prop: function() {
- var prop = new openerp.web.search.ExtendedSearchProposition(this, this.fields);
- var render = prop.render({'index': this.getChildren().length - 1});
- this.$element.find('.searchview_extended_propositions_list').append(render);
- prop.start();
+ append_proposition: function () {
+ return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
+ .appendTo(this.$element.find('ul'));
},
- start: function () {
- var _this = this;
- this.add_prop();
- this.$element.find('.searchview_extended_add_proposition').click(function () {
- _this.add_prop();
+ commit_search: function () {
+ var self = this;
+ // Get domain sections from all propositions
+ var children = this.getChildren(),
+ domain = _.invoke(children, 'get_proposition');
+ var filters = _(domain).map(function (section) {
+ return new instance.web.search.Filter({attrs: {
+ string: _.str.sprintf('%s(%s)%s',
+ section[0], section[1], section[2]),
+ domain: [section]
+ }}, self.view);
});
- this.$element.find('.searchview_extended_delete_group').click(function () {
- _this.destroy();
+ // Create Filter (& FilterGroup around it) with that domain
+ var f = new instance.web.search.FilterGroup(filters, this.view);
+ // add group to query
+ this.view.vs.searchQuery.add({
+ category: _t("Advanced"),
+ value: _(filters).map(function (f) {
+ return f.attrs.string || f.attrs.name }).join(' | '),
+ json: filters,
+ field: f,
+ app: this.view.vs
});
- },
- get_domain: function() {
- var props = _(this.getChildren()).chain().map(function(x) {
- return x.get_proposition();
- }).compact().value();
- var choice = this.$element.find(".searchview_extended_group_choice").val();
- var op = choice == "all" ? "&" : "|";
- return choice == "none" ? ['!'] : [].concat(
- _.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
- props);
- },
- destroy: function() {
- var parent = this.getParent();
- if (this.getParent().getChildren().length == 1)
- this.getParent().hide();
- this._super();
- parent.check_last_element();
- },
- set_last_group: function(is_last) {
- this.$element.toggleClass('last_group', is_last);
+ // remove all propositions
+ _.invoke(children, 'destroy');
+ // add new empty proposition
+ this.append_proposition();
+ // TODO: API on searchview
+ this.view.$element.removeClass('oe_searchview_open_drawer');
}
});
-openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** @lends openerp.web.search.ExtendedSearchProposition# */{
+instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
template: 'SearchView.extended_search.proposition',
/**
- * @constructs openerp.web.search.ExtendedSearchProposition
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.search.ExtendedSearchProposition
+ * @extends instance.web.OldWidget
*
* @param parent
* @param fields
@@ -1225,8 +1368,6 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
this.value = null;
},
start: function () {
- this.$element = $("#" + this.element_id);
- this.select_field(this.fields.length > 0 ? this.fields[0] : null);
var _this = this;
this.$element.find(".searchview_extended_prop_field").change(function() {
_this.changed();
@@ -1234,14 +1375,7 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
this.$element.find('.searchview_extended_delete_prop').click(function () {
_this.destroy();
});
- },
- destroy: function() {
- var parent;
- if (this.getParent().getChildren().length == 1)
- parent = this.getParent();
- this._super();
- if (parent)
- parent.destroy();
+ this.changed();
},
changed: function() {
var nval = this.$element.find(".searchview_extended_prop_field").val();
@@ -1267,17 +1401,11 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
}
var type = field.type;
- try {
- openerp.web.search.custom_filters.get_object(type);
- } catch (e) {
- if (! e instanceof openerp.web.KeyNotFound) {
- throw e;
- }
- type = "char";
- console.log('Unknow field type ' + e.key);
+ var obj = instance.web.search.custom_filters.get_object(type);
+ if(obj === null) {
+ obj = instance.web.search.custom_filters.get_object("char");
}
- this.value = new (openerp.web.search.custom_filters.get_object(type))
- (this);
+ this.value = new (obj) (this);
if(this.value.set_field) {
this.value.set_field(field);
}
@@ -1301,28 +1429,24 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
}
});
-openerp.web.search.ExtendedSearchProposition.Field = openerp.web.OldWidget.extend({
+instance.web.search.ExtendedSearchProposition.Field = instance.web.OldWidget.extend({
start: function () {
this.$element = $("#" + this.element_id);
}
});
-openerp.web.search.ExtendedSearchProposition.Char = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Char = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.char',
operators: [
{value: "ilike", text: _lt("contains")},
{value: "not ilike", text: _lt("doesn't contain")},
{value: "=", text: _lt("is equal to")},
- {value: "!=", text: _lt("is not equal to")},
- {value: ">", text: _lt("greater than")},
- {value: "<", text: _lt("less than")},
- {value: ">=", text: _lt("greater or equal than")},
- {value: "<=", text: _lt("less or equal than")}
+ {value: "!=", text: _lt("is not equal to")}
],
get_value: function() {
return this.$element.val();
}
});
-openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.empty',
operators: [
{value: "=", text: _lt("is equal to")},
@@ -1337,11 +1461,11 @@ openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.search.Exten
},
start: function() {
this._super();
- this.datewidget = new openerp.web.DateTimeWidget(this);
+ this.datewidget = new instance.web.DateTimeWidget(this);
this.datewidget.prependTo(this.$element);
}
});
-openerp.web.search.ExtendedSearchProposition.Date = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Date = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.empty',
operators: [
{value: "=", text: _lt("is equal to")},
@@ -1356,11 +1480,11 @@ openerp.web.search.ExtendedSearchProposition.Date = openerp.web.search.ExtendedS
},
start: function() {
this._super();
- this.datewidget = new openerp.web.DateWidget(this);
+ this.datewidget = new instance.web.DateWidget(this);
this.datewidget.prependTo(this.$element);
}
});
-openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.integer',
operators: [
{value: "=", text: _lt("is equal to")},
@@ -1372,16 +1496,16 @@ openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.search.Extend
],
get_value: function() {
try {
- return openerp.web.parse_value(this.$element.val(), {'widget': 'integer'});
+ return instance.web.parse_value(this.$element.val(), {'widget': 'integer'});
} catch (e) {
return "";
}
}
});
-openerp.web.search.ExtendedSearchProposition.Id = openerp.web.search.ExtendedSearchProposition.Integer.extend({
+instance.web.search.ExtendedSearchProposition.Id = instance.web.search.ExtendedSearchProposition.Integer.extend({
operators: [{value: "=", text: _lt("is")}]
});
-openerp.web.search.ExtendedSearchProposition.Float = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Float = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.float',
operators: [
{value: "=", text: _lt("is equal to")},
@@ -1393,13 +1517,13 @@ openerp.web.search.ExtendedSearchProposition.Float = openerp.web.search.Extended
],
get_value: function() {
try {
- return openerp.web.parse_value(this.$element.val(), {'widget': 'float'});
+ return instance.web.parse_value(this.$element.val(), {'widget': 'float'});
} catch (e) {
return "";
}
}
});
-openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Selection = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.selection',
operators: [
{value: "=", text: _lt("is")},
@@ -1412,7 +1536,7 @@ openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.search.Exte
return this.$element.val();
}
});
-openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.search.ExtendedSearchProposition.Field.extend({
+instance.web.search.ExtendedSearchProposition.Boolean = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.boolean',
operators: [
{value: "=", text: _lt("is true")},
@@ -1423,21 +1547,21 @@ openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.search.Extend
}
});
-openerp.web.search.custom_filters = new openerp.web.Registry({
- 'char': 'openerp.web.search.ExtendedSearchProposition.Char',
- 'text': 'openerp.web.search.ExtendedSearchProposition.Char',
- 'one2many': 'openerp.web.search.ExtendedSearchProposition.Char',
- 'many2one': 'openerp.web.search.ExtendedSearchProposition.Char',
- 'many2many': 'openerp.web.search.ExtendedSearchProposition.Char',
+instance.web.search.custom_filters = new instance.web.Registry({
+ 'char': 'instance.web.search.ExtendedSearchProposition.Char',
+ 'text': 'instance.web.search.ExtendedSearchProposition.Char',
+ 'one2many': 'instance.web.search.ExtendedSearchProposition.Char',
+ 'many2one': 'instance.web.search.ExtendedSearchProposition.Char',
+ 'many2many': 'instance.web.search.ExtendedSearchProposition.Char',
- 'datetime': 'openerp.web.search.ExtendedSearchProposition.DateTime',
- 'date': 'openerp.web.search.ExtendedSearchProposition.Date',
- 'integer': 'openerp.web.search.ExtendedSearchProposition.Integer',
- 'float': 'openerp.web.search.ExtendedSearchProposition.Float',
- 'boolean': 'openerp.web.search.ExtendedSearchProposition.Boolean',
- 'selection': 'openerp.web.search.ExtendedSearchProposition.Selection',
+ 'datetime': 'instance.web.search.ExtendedSearchProposition.DateTime',
+ 'date': 'instance.web.search.ExtendedSearchProposition.Date',
+ 'integer': 'instance.web.search.ExtendedSearchProposition.Integer',
+ 'float': 'instance.web.search.ExtendedSearchProposition.Float',
+ 'boolean': 'instance.web.search.ExtendedSearchProposition.Boolean',
+ 'selection': 'instance.web.search.ExtendedSearchProposition.Selection',
- 'id': 'openerp.web.search.ExtendedSearchProposition.Id'
+ 'id': 'instance.web.search.ExtendedSearchProposition.Id'
});
};
diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js
index 2c0dc3d70b4..59d1fed9345 100644
--- a/addons/web/static/src/js/view_editor.js
+++ b/addons/web/static/src/js/view_editor.js
@@ -1,15 +1,15 @@
-openerp.web.view_editor = function(openerp) {
-var _t = openerp.web._t;
-var QWeb = openerp.web.qweb;
-openerp.web.ViewEditor = openerp.web.OldWidget.extend({
+openerp.web.view_editor = function(instance) {
+var _t = instance.web._t;
+var QWeb = instance.web.qweb;
+instance.web.ViewEditor = instance.web.OldWidget.extend({
init: function(parent, element_id, dataset, view, options) {
this._super(parent);
this.element_id = element_id;
this.parent = parent;
- this.dataset = new openerp.web.DataSetSearch(this, 'ir.ui.view', null, null),
+ this.dataset = new instance.web.DataSetSearch(this, 'ir.ui.view', null, null),
this.model = dataset.model;
this.xml_element_id = 0;
- this.property = openerp.web.ViewEditor.property_widget;
+ this.property = instance.web.ViewEditor.property_widget;
this.one_object = false;
},
start: function() {
@@ -39,7 +39,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
select_view_id: self.parent.fields_view.view_id
}
};
- this.view_edit_dialog = new openerp.web.Dialog(this, {
+ this.view_edit_dialog = new instance.web.Dialog(this, {
title: action_title,
width: 850,
buttons: [
@@ -51,7 +51,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
}).open();
this.view_edit_dialog.on_close.add_last(function(){window.location.reload();});
this.main_view_id = this.parent.fields_view.view_id;
- this.action_manager = new openerp.web.ActionManager(this);
+ this.action_manager = new instance.web.ActionManager(this);
this.action_manager.appendTo(this.view_edit_dialog);
$.when(this.action_manager.do_action(action)).then(function() {
var viewmanager = self.action_manager.inner_viewmanager,
@@ -67,7 +67,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
},
on_create_view: function() {
var self = this;
- this.create_view_dialog = new openerp.web.Dialog(this, {
+ this.create_view_dialog = new instance.web.Dialog(this, {
title: _.str.sprintf(_t("Create a view (%s)"), self.model),
buttons: [
{text: _t("Save"), click: function () {
@@ -115,8 +115,8 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
},
do_save_view: function(values) {
def = $.Deferred();
- var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null);
- var model_dataset = new openerp.web.DataSetSearch(this, 'ir.model', null, null);
+ var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
+ var model_dataset = new instance.web.DataSetSearch(this, 'ir.model', null, null);
var view_string = "", field_name = false, self = this;
field_dataset.call( 'fields_get', [], function(fields) {
_.each(['name', 'x_name'], function(value) {
@@ -360,7 +360,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
edit_view: function(one_object) {
var self = this;
this.one_object = one_object;
- this.edit_xml_dialog = new openerp.web.Dialog(this, {
+ this.edit_xml_dialog = new instance.web.Dialog(this, {
title: _.str.sprintf(_t("View Editor %d - %s"), self.main_view_id, self.model),
height: '90%',
buttons: [
@@ -392,7 +392,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
action_buttons: false
}
};
- var action_manager = new openerp.web.ActionManager(self);
+ var action_manager = new instance.web.ActionManager(self);
action_manager.do_action(action);
}},
{text: _t("Close"), click: function(){
@@ -517,7 +517,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
var self = this;
var tr = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], [])[0].att_list[0];
var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0];
- var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null);
+ var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
parent_tr = $(parent_tr).find('a').text();
field_dataset.call( 'fields_get', [], function(result) {
var fields = _.keys(result);
@@ -811,7 +811,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
},
on_edit_node: function(properties){
var self = this;
- this.edit_node_dialog = new openerp.web.Dialog(this,{
+ this.edit_node_dialog = new instance.web.Dialog(this,{
title: _t("Properties"),
width: 450,
buttons: [
@@ -880,7 +880,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
this.edit_widget = [];
self.ready = $.when(self.on_groups(properties)).then(function () {
_PROPERTIES_ATTRIBUTES['groups']['selection'] = self.groups;
- var values = _.keys( openerp.web.form.widgets.map);
+ var values = _.keys( instance.web.form.widgets.map);
values.push('');
values.sort();
_PROPERTIES_ATTRIBUTES['widget']['selection'] = values;
@@ -908,8 +908,8 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
def.resolve();
}
var group_ids = [], group_names = {}, groups = [];
- var res_groups = new openerp.web.DataSetSearch(this,'res.groups', null, null),
- model_data = new openerp.web.DataSetSearch(self,'ir.model.data', null, null);
+ var res_groups = new instance.web.DataSetSearch(this,'res.groups', null, null),
+ model_data = new instance.web.DataSetSearch(self,'ir.model.data', null, null);
res_groups.read_slice([], {}).done(function (res_grp) {
_.each(res_grp, function (res) {
var key = res.id;
@@ -935,7 +935,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
{'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'},
{'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}];
this.add_widget = [];
- this.add_node_dialog = new openerp.web.Dialog(this,{
+ this.add_node_dialog = new instance.web.Dialog(this,{
title: _t("Properties"),
width: 450,
buttons: [
@@ -980,7 +980,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
});
table_selector.find("td[id^=]").attr("width","100px");
self.add_node_dialog.$element.find('#new_field').click(function() {
- model_data = new openerp.web.DataSetSearch(self,'ir.model', null, null);
+ model_data = new instance.web.DataSetSearch(self,'ir.model', null, null);
model_data.read_slice([], {domain: [['model','=', self.model]]}).then(function(result) {
self.render_new_field(result[0].id);
});
@@ -998,7 +998,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
action_buttons: true
}
};
- var action_manager = new openerp.web.ActionManager(self);
+ var action_manager = new instance.web.ActionManager(self);
$.when(action_manager.do_action(action)).then(function() {
var controller = action_manager.dialog_viewmanager.views['form'].controller;
controller.on_button_cancel.add_last(function(){
@@ -1015,7 +1015,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
});
}
});
-openerp.web.ViewEditor.Field = openerp.web.Class.extend({
+instance.web.ViewEditor.Field = instance.web.Class.extend({
init: function(view, widget) {
this.$element = view.$element;
this.dirty = false;
@@ -1041,7 +1041,7 @@ openerp.web.ViewEditor.Field = openerp.web.Class.extend({
validate: function() {
this.is_invalid = false;
try {
- var value = openerp.web.parse_value(this.get_value(), this, '');
+ var value = instance.web.parse_value(this.get_value(), this, '');
this.is_invalid = this.required && value === '';
} catch(e) {
this.is_invalid = true;
@@ -1051,7 +1051,7 @@ openerp.web.ViewEditor.Field = openerp.web.Class.extend({
return _.str.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this}))
}
});
-openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({
+instance.web.ViewEditor.FieldBoolean = instance.web.ViewEditor.Field.extend({
template : "vieweditor_boolean",
start: function() {
var self = this;
@@ -1069,7 +1069,7 @@ openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({
return this.$element.find("input[id=" + this.name + "]").is(':checked')? "1" : null;
}
});
-openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({
+instance.web.ViewEditor.FieldChar = instance.web.ViewEditor.Field.extend({
template : "vieweditor_char",
start: function () {
var self = this;
@@ -1085,7 +1085,7 @@ openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({
return this.$element.find("input[id=" + this.name + "]").val();
}
});
-openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({
+instance.web.ViewEditor.FieldSelect = instance.web.ViewEditor.Field.extend({
template : "vieweditor_selection",
start: function () {
var self = this;
@@ -1115,7 +1115,7 @@ openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({
return this.$element.find("select[id=" + this.name + "]").val();
}
});
-openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.extend({
+instance.web.ViewEditor.FieldSelectMulti = instance.web.ViewEditor.FieldSelect.extend({
start: function () {
this._super();
this.$element.find("select[id=" + this.name + "]").css('height', '100px').attr("multiple", true);
@@ -1131,7 +1131,7 @@ openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.ext
});
}
});
-openerp.web.ViewEditor.FieldFloat = openerp.web.ViewEditor.FieldChar.extend({
+instance.web.ViewEditor.FieldFloat = instance.web.ViewEditor.FieldChar.extend({
});
var _PROPERTIES = {
@@ -1196,11 +1196,11 @@ var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
'terp-sale', 'terp-tools', 'terp-administration', 'terp-hr', 'terp-partner',
'terp-project', 'terp-report', 'terp-stock', 'terp-calendar', 'terp-graph'
];
-openerp.web.ViewEditor.property_widget = new openerp.web.Registry({
- 'boolean' : 'openerp.web.ViewEditor.FieldBoolean',
- 'selection_multi' : 'openerp.web.ViewEditor.FieldSelectMulti',
- 'selection' : 'openerp.web.ViewEditor.FieldSelect',
- 'char' : 'openerp.web.ViewEditor.FieldChar',
- 'float' : 'openerp.web.ViewEditor.FieldFloat'
+instance.web.ViewEditor.property_widget = new instance.web.Registry({
+ 'boolean' : 'instance.web.ViewEditor.FieldBoolean',
+ 'selection_multi' : 'instance.web.ViewEditor.FieldSelectMulti',
+ 'selection' : 'instance.web.ViewEditor.FieldSelect',
+ 'char' : 'instance.web.ViewEditor.FieldChar',
+ 'float' : 'instance.web.ViewEditor.FieldFloat'
});
};
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 50c5dcb976a..7e5b173fd37 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -1,31 +1,32 @@
-openerp.web.form = function (openerp) {
+openerp.web.form = function (instance) {
+var _t = instance.web._t,
+ _lt = instance.web._lt;
+var QWeb = instance.web.qweb;
-var _t = openerp.web._t,
- _lt = openerp.web._lt;
-var QWeb = openerp.web.qweb;
+/** @namespace */
+instance.web.form = {};
-openerp.web.views.add('form', 'openerp.web.FormView');
-openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */{
+instance.web.views.add('form', 'instance.web.FormView');
+instance.web.FormView = instance.web.View.extend({
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
*/
searchable: false,
- readonly : false,
- form_template: "FormView",
+ template: "FormView",
display_name: _lt('Form'),
+ view_type: "form",
/**
- * @constructs openerp.web.FormView
- * @extends openerp.web.View
+ * @constructs instance.web.FormView
+ * @extends instance.web.View
*
- * @param {openerp.web.Session} session the current openerp session
- * @param {openerp.web.DataSet} dataset the dataset this view will work with
+ * @param {instance.web.Session} session the current openerp session
+ * @param {instance.web.DataSet} dataset the dataset this view will work with
* @param {String} view_id the identifier of the OpenERP view object
* @param {Object} options
- * - sidebar : [true|false]
* - resize_textareas : [true|false|max_height]
*
- * @property {openerp.web.Registry} registry=openerp.web.form.widgets widgets registry for this form view instance
+ * @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance
*/
init: function(parent, dataset, view_id, options) {
this._super(parent);
@@ -34,98 +35,86 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.model = dataset.model;
this.view_id = view_id || false;
this.fields_view = {};
- this.widgets = {};
- this.widgets_counter = 0;
this.fields = {};
this.fields_order = [];
this.datarecord = {};
this.default_focus_field = null;
this.default_focus_button = null;
- this.registry = openerp.web.form.widgets;
+ this.fields_registry = instance.web.form.widgets;
+ this.tags_registry = instance.web.form.tags;
this.has_been_loaded = $.Deferred();
- this.$form_header = null;
this.translatable_fields = [];
_.defaults(this.options, {
- "not_interactible_on_create": false
+ "not_interactible_on_create": false,
+ "initial_mode": "view",
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
this.on_change_mutex = new $.Mutex();
this.reload_mutex = new $.Mutex();
- },
- start: function() {
- this._super();
- return this.init_view();
- },
- init_view: function() {
- if (this.embedded_view) {
- var def = $.Deferred().then(this.on_loaded);
- var self = this;
- $.async_when().then(function() {def.resolve(self.embedded_view);});
- return def.promise();
- } else {
- var context = new openerp.web.CompoundContext(this.dataset.get_context());
- return this.rpc("/web/view/load", {
- "model": this.model,
- "view_id": this.view_id,
- "view_type": "form",
- toolbar: this.options.sidebar,
- context: context
- }, this.on_loaded);
- }
+ this.mode = null;
+ this.rendering_engine = new instance.web.form.FormRenderingEngineReadonly(this);
+ this.qweb = null; // A QWeb instance will be created if the view is a QWeb template
},
destroy: function() {
- if (this.sidebar) {
- this.sidebar.attachments.destroy();
- this.sidebar.destroy();
- }
- _.each(this.widgets, function(w) {
+ _.each(this.get_widgets(), function(w) {
w.destroy();
});
this._super();
},
- reposition: function ($e) {
- this.$element = $e;
- this.on_loaded();
- },
on_loaded: function(data) {
var self = this;
if (!data) {
- throw "No data provided.";
+ throw new Error("No data provided.");
}
- if (this.root_frame) {
- throw "Form view does not support multiple calls to on_loaded";
+ if (this.arch) {
+ throw "Form view does not support multiple calls to on_loaded";
}
this.fields_order = [];
this.fields_view = data;
- this.rendered = QWeb.render(this.form_template, {'widget': this});
- this.$element.html(this.rendered);
-
- this.root_frame = instanciate_widget(this.registry.get_object('frame'), this, this.fields_view.arch);
- var to_append = $(".oe_form_header", this.$element);
- this.root_frame.appendTo(to_append.length > 0 ? to_append : this.$element);
- this.root_frame.$element.children().unwrap();
-
- this.$form_header = this.$element.find('.oe_form_header:first');
- this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
+ this.rendering_engine.set_fields_registry(this.fields_registry);
+ this.rendering_engine.set_tags_registry(this.tags_registry);
+ if (!this.extract_qweb_template(data)) {
+ this.rendering_engine.set_fields_view(data);
+ var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ this.rendering_engine.render_to($dest);
+ }
+
+ this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar');
+
+ this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self}));
+ if (this.options.$buttons) {
+ this.$buttons.appendTo(this.options.$buttons);
+ } else {
+ this.$element.find('.oe_form_buttons').replaceWith(this.$buttons);
+ }
+ this.$buttons
+ .on('click','button.oe_form_button_save',this.on_button_save)
+ .on('click','button.oe_form_button_cancel',this.on_button_cancel)
+ .on('click','button.oe_form_button_edit',this.on_button_edit)
+ .on('click','button.oe_form_button_create',this.on_button_create)
+ .on('click','button.oe_form_button_duplicate',this.on_button_duplicate)
+ .on('click','button.oe_form_button_delete',this.on_button_delete);
+
+ this.$pager = $(QWeb.render("FormView.pager", {'widget':self}));
+ if (this.options.$pager) {
+ this.$pager.appendTo(this.options.$pager);
+ } else {
+ this.$element.find('.oe_form_pager').replaceWith(this.$pager);
+ }
+ this.$pager.on('click','a[data-pager-action]',function() {
var action = $(this).data('pager-action');
self.on_pager_action(action);
});
- this.$form_header.find('button.oe_form_button_save').click(this.on_button_save);
- this.$form_header.find('button.oe_form_button_cancel').click(this.on_button_cancel);
-
- if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
- this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
- this.sidebar.start();
- this.sidebar.do_unfold();
- this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this);
- this.sidebar.add_toolbar(this.fields_view.toolbar);
- this.set_common_sidebar_sections(this.sidebar);
-
- this.sidebar.add_section(_t('Customize'), 'customize');
- this.sidebar.add_items('customize', [{
+ if (!this.sidebar && this.options.$sidebar) {
+ this.sidebar = new instance.web.Sidebar(this);
+ this.sidebar.appendTo(this.$sidebar);
+ if(this.fields_view.toolbar) {
+ this.sidebar.add_toolbar(this.fields_view.toolbar);
+ }
+ this.sidebar.add_items('other', [{
label: _t('Set Default'),
form: this,
callback: function (item) {
@@ -133,9 +122,61 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
}]);
}
+ this.on("change:mode", this, this.switch_mode);
+ this.set({mode: this.options.initial_mode});
this.has_been_loaded.resolve();
+ return $.when();
+ },
+ 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();
+ this.qweb.add_template(instance.web.json_node_to_xml(child));
+ 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);
+ fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild);
+ return fvg;
+ },
+ get_qweb_context: function(record) {
+ var self = this,
+ new_record = {};
+ _.each(record, function(value_, name) {
+ var r = _.clone(self.fields_view.fields[name] || {});
+ if ((r.type === 'date' || r.type === 'datetime') && value_) {
+ r.raw_value = instance.web.auto_str_to_date(value_);
+ } else {
+ r.raw_value = value_;
+ }
+ r.value = instance.web.format_value(value_, r);
+ 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 = [];
+ this.$element.find('.oe_form_container').empty();
},
-
do_load_state: function(state, warm) {
if (state.id && this.datarecord.id != state.id) {
if (!this.dataset.get_id_index(state.id)) {
@@ -147,9 +188,18 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
}
},
-
do_show: function () {
var self = this;
+ if (this.sidebar) {
+ this.sidebar.$element.show();
+ }
+ if (this.$buttons) {
+ this.$buttons.show();
+ this.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
+ }
+ if (this.$pager) {
+ this.$pager.show();
+ }
this.$element.show().css('visibility', 'hidden');
this.$element.removeClass('oe_form_dirty');
return this.has_been_loaded.pipe(function() {
@@ -165,17 +215,20 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
result.pipe(function() {
self.$element.css('visibility', 'visible');
});
- if (self.sidebar) {
- self.sidebar.$element.show();
- }
return result;
});
},
do_hide: function () {
- this._super();
if (this.sidebar) {
this.sidebar.$element.hide();
}
+ if (this.$buttons) {
+ this.$buttons.hide();
+ }
+ if (this.$pager) {
+ this.$pager.hide();
+ }
+ this._super();
},
on_record_loaded: function(record) {
var self = this, set_values = [];
@@ -185,13 +238,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
this.datarecord = record;
+ if (this.qweb) {
+ this.kill_current_form();
+ this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record));
+ var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ this.rendering_engine.render_to($dest);
+ }
+
_(this.fields).each(function (field, f) {
- field.reset();
+ field._dirty_flag = false;
var result = field.set_value(self.datarecord[f] || false);
set_values.push(result);
- $.when(result).then(function() {
- field.validate();
- });
});
return $.when.apply(null, set_values).pipe(function() {
if (!record.id) {
@@ -200,7 +257,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
_.each(self.fields_order, function(field_name) {
if (record[field_name] !== undefined) {
var field = self.fields[field_name];
- field.dirty = true;
+ field._dirty_flag = true;
self.do_onchange(field);
}
});
@@ -209,7 +266,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
self.is_initialized.resolve();
self.do_update_pager(record.id == null);
if (self.sidebar) {
- self.sidebar.attachments.do_update();
+ self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
}
if (self.default_focus_field) {
self.default_focus_field.focus();
@@ -218,20 +275,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
self.do_push_state({id:record.id});
}
self.$element.removeClass('oe_form_dirty');
+ self.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
});
},
on_form_changed: function() {
- for (var w in this.widgets) {
- w = this.widgets[w];
- w.process_modifiers();
- if (w.field) {
- w.validate();
- }
- w.update_dom();
- }
+ this.trigger("view_content_has_changed");
},
do_notify_change: function() {
this.$element.addClass('oe_form_dirty');
+ this.$buttons.find('.oe_form_button_save').addClass('oe_form_button_save_dirty');
},
on_pager_action: function(action) {
if (this.can_be_discarded()) {
@@ -253,11 +305,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
},
do_update_pager: function(hide_index) {
- var $pager = this.$form_header.find('div.oe_form_pager');
var index = hide_index ? '-' : this.dataset.index + 1;
- $pager.find('button').prop('disabled', this.dataset.ids.length < 2);
- $pager.find('span.oe_pager_index').html(index);
- $pager.find('span.oe_pager_count').html(this.dataset.ids.length);
+ 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);
},
parse_on_change: function (on_change, widget) {
var self = this;
@@ -278,7 +329,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
'None': function () {return null;},
'context': function (i) {
context_index = i;
- var ctx = new openerp.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {});
+ var ctx = new instance.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {});
return ctx;
}
};
@@ -296,8 +347,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
// form field
if (self.fields[field]) {
- var value = self.fields[field].get_on_change_value();
- return value == null ? false : value;
+ var value_ = self.fields[field].get_value();
+ return value_ == null ? false : value_;
}
// parent field
var splitted = field.split('.');
@@ -360,17 +411,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
if (widget.field['change_default']) {
- var fieldname = widget.name, value;
+ var fieldname = widget.name, value_;
if (response.value && (fieldname in response.value)) {
// Use value from onchange if onchange executed
- value = response.value[fieldname];
+ value_ = response.value[fieldname];
} else {
// otherwise get form value for field
- value = self.fields[fieldname].get_on_change_value();
+ value_ = self.fields[fieldname].get_value();
}
- var condition = fieldname + '=' + value;
+ var condition = fieldname + '=' + value_;
- if (value) {
+ if (value_) {
can_process_onchange = self.rpc({
url: '/web/dataset/call',
async: false
@@ -411,10 +462,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var field = this.fields[f];
// If field is not defined in the view, just ignore it
if (field) {
- var value = result.value[f];
- if (field.get_value() != value) {
- field.set_value(value);
- field.dirty = true;
+ var value_ = result.value[f];
+ if (field.get_value() != value_) {
+ field.set_value(value_);
+ field._dirty_flag = true;
if (!_.contains(processed, field.name)) {
this.do_onchange(field, processed);
}
@@ -424,7 +475,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.on_form_changed();
}
if (!_.isEmpty(result.warning)) {
- openerp.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), {
+ instance.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), {
modal: true,
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -447,19 +498,38 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
return $.Deferred().reject();
}
},
+ switch_mode: function() {
+ var mode = this.get("mode");
+ var self = this;
+ if(mode=="view") {
+ self.$buttons.find('.oe_form_buttons_edit').hide();
+ self.$buttons.find('.oe_form_buttons_view').show();
+ _.each(this.fields,function(field){
+ field.set({"force_readonly": true});
+ });
+ } else {
+ self.$buttons.find('.oe_form_buttons_edit').show();
+ self.$buttons.find('.oe_form_buttons_view').hide();
+ _.each(this.fields,function(field){
+ field.set({"force_readonly": false});
+ });
+ }
+ self.mode = mode;
+ },
on_button_save: function() {
var self = this;
return this.do_save().then(function(result) {
- self.do_prev_view({'created': result.created, 'default': 'page'});
+ self.set({mode: "view"});
});
},
on_button_cancel: function() {
if (this.can_be_discarded()) {
- return this.do_prev_view({'default': 'page'});
+ this.set({mode: "view"});
}
},
on_button_new: function() {
var self = this;
+ this.set({mode: "edit"});
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
if (self.can_be_discarded()) {
@@ -477,6 +547,44 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
});
return def.promise();
},
+ on_button_edit: function() {
+ return this.set({mode: "edit"});
+ },
+ on_button_create: function() {
+ this.dataset.index = null;
+ this.do_show();
+ },
+ 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() {
+ return self.set({mode: "edit"});
+ }).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();
+ },
can_be_discarded: function() {
return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
},
@@ -499,11 +607,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
f = self.fields[f];
if (!f.is_valid()) {
form_invalid = true;
- f.update_dom(true);
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.name !== 'id' && !f.readonly && (!self.datarecord.id || f.is_dirty())) {
+ } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) {
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
@@ -511,20 +618,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
}
if (form_invalid) {
+ self.set({'display_invalid_fields': true});
first_invalid_field.focus();
self.on_invalid();
return $.Deferred().reject();
} else {
+ self.set({'display_invalid_fields': false});
var save_deferral;
if (!self.datarecord.id) {
//console.log("FormView(", self, ") : About to create", values);
save_deferral = self.dataset.create(values).pipe(function(r) {
return self.on_created(r, undefined, prepend_on_create);
}, null);
- } else if (_.isEmpty(values)) {
+ } else if (_.isEmpty(values) && ! self.force_dirty) {
//console.log("FormView(", self, ") : Nothing to save");
save_deferral = $.Deferred().resolve({}).promise();
} else {
+ self.force_dirty = false;
//console.log("FormView(", self, ") : About to save", values);
save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
return self.on_saved(r);
@@ -542,7 +652,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var msg = "<ul>";
_.each(this.fields, function(f) {
if (!f.is_valid()) {
- msg += "<li>" + f.string + "</li>";
+ msg += "<li>" + f.node.attrs.string + "</li>";
}
});
msg += "</ul>";
@@ -580,14 +690,14 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
} else {
- this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
+ this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
this.dataset.index = 0;
}
this.do_update_pager();
if (this.sidebar) {
- this.sidebar.attachments.do_update();
+ this.sidebar.do_attachement_update(this.dataset, this.datarecord.id);
}
- //openerp.log("The record has been created with id #" + this.datarecord.id);
+ //instance.log("The record has been created with id #" + this.datarecord.id);
this.reload();
return $.when(_.extend(r, {created: true})).then(success);
}
@@ -598,6 +708,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
reload: function() {
var self = this;
return this.reload_mutex.exec(function() {
+ if (self.dataset.index == null) {
+ self.do_prev_view();
+ return $.Deferred().reject().promise();
+ }
if (self.dataset.index == null || self.dataset.index < 0) {
return $.when(self.on_button_new());
} else {
@@ -607,15 +721,20 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
});
},
+ get_widgets: function() {
+ return _.filter(this.getChildren(), function(obj) {
+ return obj instanceof instance.web.form.Widget;
+ });
+ },
get_fields_values: function(blacklist) {
blacklist = blacklist || [];
var values = {};
var ids = this.get_selected_ids();
values["id"] = ids.length > 0 ? ids[0] : false;
- _.each(this.fields, function(value, key) {
+ _.each(this.fields, function(value_, key) {
if (_.include(blacklist, key))
return;
- var val = value.get_value();
+ var val = value_.get_value();
values[key] = val;
});
return values;
@@ -632,8 +751,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
});
},
is_dirty: function() {
- return _.any(this.fields, function (value) {
- return value.is_dirty();
+ return _.any(this.fields, function (value_) {
+ return value_._dirty_flag;
});
},
is_interactible_record: function() {
@@ -642,7 +761,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
if (this.options.not_interactible_on_create)
return false;
} else if (typeof(id) === "string") {
- if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
+ if(instance.web.BufferedDataSet.virtual_id_regex.test(id))
return false;
}
return true;
@@ -654,12 +773,12 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var self = this;
var fields = _.chain(this.fields)
.map(function (field, name) {
- var value = field.get_value();
+ var value_ = field.get_value();
// ignore fields which are empty, invisible, readonly, o2m
// or m2m
- if (!value
- || field.invisible
- || field.readonly
+ if (!value_
+ || field.get('invisible')
+ || field.get("readonly")
|| field.field.type === 'one2many'
|| field.field.type === 'many2many') {
return false;
@@ -668,33 +787,33 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
switch(field.field.type) {
case 'selection':
displayed = _(field.values).find(function (option) {
- return option[0] === value;
+ return option[0] === value_;
})[1];
break;
case 'many2one':
- displayed = field.value[1] || value;
+ displayed = value_;
break;
default:
- displayed = value;
+ displayed = value_;
}
return {
name: name,
- string: field.string,
- value: value,
+ string: field.node_atts.string,
+ value: value_,
displayed: displayed,
// convert undefined to false
change_default: !!field.field.change_default
}
})
.compact()
- .sortBy(function (field) { return field.string; })
+ .sortBy(function (field) { return field.node_atts.string; })
.value();
var conditions = _.chain(fields)
.filter(function (field) { return field.change_default; })
.value();
- var d = new openerp.web.Dialog(this, {
+ var d = new instance.web.Dialog(this, {
title: _t("Set Default"),
args: {
fields: fields,
@@ -706,12 +825,12 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var $defaults = d.$element.find('#formview_default_fields');
var field_to_set = $defaults.val();
if (!field_to_set) {
- $defaults.parent().addClass('invalid');
+ $defaults.parent().addClass('oe_form_invalid');
return;
}
var condition = d.$element.find('#formview_default_conditions').val(),
all_users = d.$element.find('#formview_default_all').is(':checked');
- new openerp.web.DataSet(self, 'ir.values').call(
+ new instance.web.DataSet(self, 'ir.values').call(
'set_default', [
self.dataset.model,
field_to_set,
@@ -725,9 +844,368 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
});
d.template = 'FormView.set_default';
d.open();
- }
+ },
+ register_field: function(field, name) {
+ this.fields[name] = field;
+ this.fields_order.push(name);
+ if (this.get_field(name).translate) {
+ this.translatable_fields.push(field);
+ }
+ field.on('changed_value', this, function() {
+ field._dirty_flag = true;
+ if (field.is_syntax_valid()) {
+ this.do_onchange(field);
+ this.on_form_changed(true);
+ this.do_notify_change();
+ }
+ });
+ },
+ get_field: function(field_name) {
+ return this.fields_view.fields[field_name];
+ },
+ is_create_mode: function() {
+ return !this.datarecord.id;
+ },
});
-openerp.web.FormDialog = openerp.web.Dialog.extend({
+
+/**
+ * Interface to be implemented by rendering engines for the form view.
+ */
+instance.web.form.FormRenderingEngineInterface = {
+ set_fields_view: function(fields_view) {},
+ set_fields_registry: function(fields_registry) {},
+ render_to: function($element) {},
+};
+
+/**
+ * Default rendering engine for the form view.
+ *
+ * It is necessary to set the view using set_view() before usage.
+ */
+instance.web.form.FormRenderingEngine = instance.web.Class.extend({
+ init: function(view) {
+ this.view = view;
+ },
+ set_fields_view: function(fvg) {
+ this.fvg = fvg;
+ },
+ set_tags_registry: function(tags_registry) {
+ this.tags_registry = tags_registry;
+ },
+ set_fields_registry: function(fields_registry) {
+ this.fields_registry = fields_registry;
+ },
+ render_to: function($target) {
+ var self = this;
+ this.$target = $target;
+
+ // 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
+ var xml = instance.web.json_node_to_xml(this.fvg.arch);
+ this.$form = $('<div class="oe_form">' + xml + '</div>');
+ if (this.fvg.arch.attrs && this.fvg.arch.attrs['layout'] !== 'manual') {
+ this.$form.attr('layout', 'auto');
+ }
+
+ this.fields_to_init = [];
+ this.tags_to_init = [];
+ this.labels = {};
+ this.process(this.$form);
+
+ this.$form.appendTo(this.$target);
+
+ _.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.");
+ }
+ var obj = self.fields_registry.get_any([$elem.attr('widget'), self.fvg.fields[name].type]);
+ if (!obj) {
+ throw new Error("Widget type '"+ key + "' is not implemented");
+ }
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ var $label = self.labels[$elem.attr("name")];
+ if ($label) {
+ w.set_input_id($label.attr("for"));
+ }
+ self.alter_field(w);
+ self.view.register_field(w, $elem.attr("name"));
+ w.replace($elem);
+ });
+ _.each(this.tags_to_init, function($elem) {
+ var tag_name = $elem[0].tagName.toLowerCase();
+ var obj = self.tags_registry.get_object(tag_name);
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ w.replace($elem);
+ })
+ // TODO: return a deferred
+ },
+ render_element: function(template, layout/* dictionaries */) {
+ var dicts = [].slice.call(arguments).slice(2);
+ dicts.unshift({ 'layout' : layout });
+ var dict = _.extend.apply(_, dicts);
+ dict['classnames'] = dict['class'] || ''; // class is a reserved word and might caused problem to Safari when used from QWeb
+ var alt_template = template + '.' + layout;
+ template = QWeb.has_template(alt_template) ? alt_template : template;
+ return $(QWeb.render(template, dict));
+ },
+ alter_field: function(field) {
+ },
+ toggle_layout_debugging: function() {
+ if (!this.$target.has('.oe_layout_debug_cell:first').length) {
+ this.$target.find('[title]').removeAttr('title');
+ this.$target.find('.oe_form_group_cell').each(function() {
+ var text = 'W:' + ($(this).attr('width') || '') + ' - C:' + $(this).attr('colspan');
+ $(this).attr('title', text);
+ });
+ }
+ this.$target.toggleClass('oe_layout_debugging');
+ },
+ process: function($tag, layout) {
+ var self = this;
+ if ($tag.attr('layout') === 'auto') {
+ $tag.addClass('oe_form_autolayout');
+ }
+ layout = $tag.attr('layout') || layout || 'auto';
+ $tag.removeAttr('layout');
+ var tagname = $tag[0].nodeName.toLowerCase();
+ if (this.tags_registry.contains(tagname)) {
+ this.tags_to_init.push($tag);
+ return $tag;
+ }
+ var fn = self['process_' + tagname];
+ if (fn) {
+ var args = [].slice.call(arguments);
+ args[0] = $tag;
+ args[1] = layout;
+ return fn.apply(self, args);
+ } else {
+ // generic tag handling, just process children
+ $tag.children().each(function() {
+ self.process($(this), layout);
+ });
+ self.handle_common_properties($tag, $tag);
+ $tag.removeAttr("modifiers");
+ return $tag;
+ }
+ },
+ process_sheet: function() {
+ this.process_form.apply(this, arguments);
+ },
+ process_form: function($form, layout) {
+ var $new_form = this.render_element('FormRenderingForm', layout, $form.getAttributes());
+ var $dst = (layout === 'auto') ? $new_form.find('group:first') : $new_form;
+ $new_form.attr("modifiers", $form.attr("modifiers"));
+ $form.children().appendTo($dst);
+ if ($form[0] === this.$form[0]) {
+ // If root element, replace it
+ this.$form = $new_form;
+ } else {
+ $form.before($new_form).remove();
+ }
+ this.process($new_form);
+ },
+ preprocess_field: function($field) {
+ var self = this;
+ var name = $field.attr('name'),
+ field_colspan = parseInt($field.attr('colspan'), 10),
+ field_modifiers = JSON.parse($field.attr('modifiers') || '{}');
+
+ if ($field.attr('nolabel') === '1')
+ return;
+ $field.attr('nolabel', '1');
+ var found = false;
+ this.$form.find('label[for="' + name + '"]').each(function(i ,el) {
+ $(el).parents().each(function(unused, tag) {
+ var name = tag.tagName.toLowerCase();
+ if (name === "field" || name in self.tags_registry.map)
+ found = true;
+ });
+ });
+ if (found)
+ return;
+
+ $label = $('<label/>').attr({
+ 'for' : name,
+ "modifiers": JSON.stringify({invisible: field_modifiers.invisible}),
+ "string": $field.attr('string'),
+ "help": $field.attr('help'),
+ });
+ $label.insertBefore($field);
+ if (field_colspan > 1) {
+ $field.attr('colspan', field_colspan - 1);
+ }
+ return $label;
+ },
+ process_field: function($field, layout) {
+ var $label = this.preprocess_field($field);
+ if ($label)
+ this.process($label, layout);
+ this.fields_to_init.push($field);
+ return $field;
+ },
+ process_group: function($group, layout) {
+ var self = this;
+ if ($group.parent().is('.oe_form_group_cell')) {
+ $group.parent().addClass('oe_form_group_nested');
+ }
+ $group.children('field').each(function() {
+ self.preprocess_field($(this));
+ });
+ var $new_group = this.render_element('FormRenderingGroup', layout, $group.getAttributes()),
+ $table;
+ if ($new_group.is('table')) {
+ $table = $new_group;
+ } else {
+ $table = $new_group.find('table:first');
+ }
+ $table.addClass('oe_form_group');
+ var $tr, $td,
+ cols = parseInt($group.attr('col') || 4, 10),
+ row_cols = cols;
+
+ var children = [];
+ $group.children().each(function(a,b,c) {
+ var $child = $(this),
+ colspan = parseInt($child.attr('colspan') || 1, 10),
+ tagName = $child[0].tagName.toLowerCase();
+ if (tagName === 'newline') {
+ $tr = null;
+ return;
+ }
+ if (!$tr || row_cols < colspan) {
+ $tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table);
+ row_cols = cols;
+ }
+ row_cols -= colspan;
+
+ $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan);
+ // invisibility transfer
+ var field_modifiers = JSON.parse($child.attr('modifiers') || '{}');
+ var invisible = field_modifiers.invisible;
+ field_modifiers.invisible = undefined;
+ $child.attr('modifiers', JSON.stringify(field_modifiers));
+ self.handle_common_properties($td, $("<dummy>").attr("modifiers", JSON.stringify({invisible: invisible})));
+
+ $tr.append($td.append($child));
+ children.push($child[0]);
+ });
+ if (row_cols && $td) {
+ $td.attr('colspan', parseInt($td.attr('colspan'), 10) + row_cols);
+ }
+ $group.before($new_group).remove();
+
+ // Now compute width of cells
+ $table.find('> tbody > tr').each(function() {
+ var to_compute = [],
+ row_cols = cols,
+ total = 100;
+ $(this).children().each(function() {
+ var $td = $(this),
+ $child = $td.children(':first');
+ switch ($child[0].tagName.toLowerCase()) {
+ case 'separator':
+ if ($child.attr('orientation') === 'vertical') {
+ $td.addClass('oe_vertical_separator').attr('width', '1');
+ $td.empty();
+ row_cols--;
+ }
+ break;
+ case 'label':
+ if ($child.attr('for')) {
+ $td.attr('width', '1%');
+ row_cols--;
+ total--;
+ }
+ break;
+ default:
+ to_compute.push($td);
+ }
+ });
+ var unit = Math.floor(total / row_cols);
+ _.each(to_compute, function($td, i) {
+ var width = parseInt($td.attr('colspan'), 10) * unit;
+ $td.attr('width', ((i == to_compute.length - 1) ? total : width) + '%');
+ total -= width;
+ });
+ });
+ _.each(children, function(el) {
+ self.process($(el));
+ });
+ this.handle_common_properties($new_group, $group);
+ return $new_group;
+ },
+ process_notebook: function($notebook, layout) {
+ var self = this;
+ var pages = [];
+ $notebook.find('> page').each(function() {
+ var $page = $(this);
+ var page_attrs = $page.getAttributes();
+ page_attrs.id = _.uniqueId('notebook_page_');
+ pages.push(page_attrs);
+ var $new_page = self.render_element('FormRenderingNotebookPage', layout, page_attrs);
+ var $dst = (layout === 'auto') ? $new_page.find('group:first') : $new_page;
+ $page.children().appendTo($dst);
+ $page.before($new_page).remove();
+ self.handle_common_properties($new_page, $page);
+ });
+ var $new_notebook = this.render_element('FormRenderingNotebook', layout, { pages : pages });
+ $notebook.children().appendTo($new_notebook);
+ $notebook.before($new_notebook).remove();
+ $new_notebook.children().each(function() {
+ self.process($(this));
+ });
+ $new_notebook.tabs();
+ this.handle_common_properties($new_notebook, $notebook);
+ return $new_notebook;
+ },
+ process_separator: function($separator, layout) {
+ var $new_separator = this.render_element('FormRenderingSeparator', layout, $separator.getAttributes());
+ $separator.before($new_separator).remove();
+ this.handle_common_properties($new_separator, $separator);
+ return $new_separator;
+ },
+ process_label: function($label, layout) {
+ var name = $label.attr("for"),
+ field_orm = this.fvg.fields[name];
+ var dict = {
+ string: $label.attr('string') || (field_orm || {}).string || '',
+ help: $label.attr('help') || (field_orm || {}).help || '',
+ _for: name ? _.uniqueId('oe-field-input-') : undefined,
+ };
+ var align = parseFloat(dict.align);
+ if (isNaN(align) || align === 1) {
+ align = 'right';
+ } else if (align === 0) {
+ align = 'left';
+ } else {
+ align = 'center';
+ }
+ dict.align = align;
+ var $new_label = this.render_element('FormRenderingLabel', layout, dict);
+ $label.before($new_label).remove();
+ this.handle_common_properties($new_label, $label);
+ if (name) {
+ this.labels[name] = $new_label;
+ }
+ return $new_label;
+ },
+ handle_common_properties: function($new_element, $node) {
+ var str_modifiers = $node.attr("modifiers") || "{}"
+ var modifiers = JSON.parse(str_modifiers);
+ if (modifiers.invisible !== undefined)
+ new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element);
+ $new_element.addClass($node.attr("class") || "");
+ },
+});
+
+instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({
+ alter_field: function(field) {
+ field.set({"force_readonly": true});
+ },
+});
+
+instance.web.form.FormDialog = instance.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
this.dataset = dataset;
@@ -736,8 +1214,7 @@ openerp.web.FormDialog = openerp.web.Dialog.extend({
},
start: function() {
this._super();
- this.form = new openerp.web.FormView(this, this.dataset, this.view_id, {
- sidebar: false,
+ this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
pager: false
});
this.form.appendTo(this.$element);
@@ -758,62 +1235,7 @@ openerp.web.FormDialog = openerp.web.Dialog.extend({
}
});
-/** @namespace */
-openerp.web.form = {};
-
-openerp.web.form.SidebarAttachments = openerp.web.OldWidget.extend({
- init: function(parent, form_view) {
- var $section = parent.add_section(_t('Attachments'), 'attachments');
- this.$div = $('<div class="oe-sidebar-attachments"></div>');
- $section.append(this.$div);
-
- this._super(parent, $section.attr('id'));
- this.view = form_view;
- },
- do_update: function() {
- if (!this.view.datarecord.id) {
- this.on_attachments_loaded([]);
- } else {
- (new openerp.web.DataSetSearch(
- this, 'ir.attachment', this.view.dataset.get_context(),
- [
- ['res_model', '=', this.view.dataset.model],
- ['res_id', '=', this.view.datarecord.id],
- ['type', 'in', ['binary', 'url']]
- ])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
- }
- },
- on_attachments_loaded: function(attachments) {
- this.attachments = attachments;
- this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
- this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
- this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
- },
- on_attachment_changed: function(e) {
- window[this.element_id + '_iframe'] = this.do_update;
- var $e = $(e.target);
- if ($e.val() != '') {
- this.$element.find('form.oe-binary-form').submit();
- $e.parent().find('input[type=file]').prop('disabled', true);
- $e.parent().find('button').prop('disabled', true).find('img, span').toggle();
- }
- },
- on_attachment_delete: function(e) {
- var self = this, $e = $(e.currentTarget);
- var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
- if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) {
- this.rpc('/web/dataset/unlink', {
- model: 'ir.attachment',
- ids: [parseInt($e.attr('data-id'))]
- }, function(r) {
- $e.parent().remove();
- self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
- });
- }
- }
-});
-
-openerp.web.form.compute_domain = function(expr, fields) {
+instance.web.form.compute_domain = function(expr, fields) {
var stack = [];
for (var i = expr.length - 1; i >= 0; i--) {
var ex = expr[i];
@@ -842,7 +1264,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
_t("Unknown field %s in domain %s"),
ex[0], JSON.stringify(expr)));
}
- var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
+ var field_value = field.get_value ? field.get_value() : field.value;
var op = ex[1];
var val = ex[2];
@@ -884,60 +1306,84 @@ openerp.web.form.compute_domain = function(expr, fields) {
return _.all(stack, _.identity);
};
-openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
- form_template: 'Widget',
+/**
+ * Must be applied over an class already possessing the GetterSetterMixin.
+ *
+ * Apply the result of the "invisible" domain to this.$element.
+ */
+instance.web.form.InvisibilityChangerMixin = {
+ init: function(field_manager, invisible_domain) {
+ this._ic_field_manager = field_manager
+ this._ic_invisible_modifier = invisible_domain;
+ this._ic_field_manager.on("view_content_has_changed", this, function() {
+ var result = this._ic_invisible_modifier === undefined ? false :
+ instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields);
+ this.set({"invisible": result});
+ });
+ this.set({invisible: this._ic_invisible_modifier === true});
+ },
+ start: function() {
+ var check_visibility = function() {
+ if (this.get("invisible")) {
+ this.$element.hide();
+ } else {
+ this.$element.show();
+ }
+ };
+ this.on("change:invisible", this, check_visibility);
+ _.bind(check_visibility, this)();
+ },
+};
+
+instance.web.form.InvisibilityChanger = instance.web.Class.extend(_.extend({}, instance.web.GetterSetterMixin, instance.web.form.InvisibilityChangerMixin, {
+ init: function(parent, field_manager, invisible_domain, $element) {
+ this.setParent(parent);
+ instance.web.GetterSetterMixin.init.call(this);
+ instance.web.form.InvisibilityChangerMixin.init.call(this, field_manager, invisible_domain);
+ this.$element = $element;
+ this.start();
+ },
+}));
+
+instance.web.form.Widget = instance.web.Widget.extend(_.extend({}, instance.web.form.InvisibilityChangerMixin, {
/**
- * @constructs openerp.web.form.Widget
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.form.Widget
+ * @extends instance.web.Widget
*
* @param view
* @param node
*/
init: function(view, node) {
+ this._super(view);
this.view = view;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
- this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
- this.type = this.type || node.tag;
- this.element_name = this.element_name || this.type;
-
- this._super(view);
-
- this.children = node.children;
- this.colspan = parseInt(node.attrs.colspan || 1, 10);
- this.decrease_max_width = 0;
-
- this.string = this.string || node.attrs.string;
- this.help = this.help || node.attrs.help;
- this.invisible = this.modifiers['invisible'] === true;
- this.classname = 'oe_form_' + this.type;
-
- this.align = parseFloat(this.node.attrs.align);
- if (isNaN(this.align) || this.align === 1) {
- this.align = 'right';
- } else if (this.align === 0) {
- this.align = 'left';
- } else {
- this.align = 'center';
- }
+ instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible);
- this.width = this.node.attrs.width;
+ this.view.on("view_content_has_changed", this, this.process_modifiers);
+ },
+ renderElement: function() {
+ this._super();
+ this.$element.addClass(this.node.attrs["class"] || "");
+ },
+ start: function() {
+ this._super();
+ instance.web.form.InvisibilityChangerMixin.start.call(this);
},
destroy: function() {
- this._super.apply(this, arguments);
$.fn.tipsy.clear();
+ this._super.apply(this, arguments);
},
process_modifiers: function() {
- var compute_domain = openerp.web.form.compute_domain;
+ var compute_domain = instance.web.form.compute_domain;
+ var to_set = {};
for (var a in this.modifiers) {
- this[a] = compute_domain(this.modifiers[a], this.view.fields);
+ if (!_.include(["invisible"], a)) {
+ var val = compute_domain(this.modifiers[a], this.view.fields);
+ to_set[a] = val;
+ }
}
- },
- update_dom: function() {
- this.$element.toggle(!this.invisible);
- },
- renderElement: function() {
- this.$element.html(QWeb.render(this.form_template, { "widget": this }));
+ this.set(to_set);
},
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
@@ -947,12 +1393,12 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
delayOut: 0,
fade: true,
title: function() {
- var template = widget.form_template + '.tooltip';
+ var template = widget.template + '.tooltip';
if (!QWeb.has_template(template)) {
template = 'WidgetLabel.tooltip';
}
return QWeb.render(template, {
- debug: openerp.connection.debug,
+ debug: instance.connection.debug,
widget: widget
})},
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
@@ -979,7 +1425,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
},
_build_eval_context: function(blacklist) {
var a_dataset = this.view.dataset;
- return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
+ return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
},
/**
* Builds a new context usable for operations related to fields by merging
@@ -994,7 +1440,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
if (v_context.__ref || true) { //TODO: remove true
var fields_values = this._build_eval_context(blacklist);
- v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values);
+ v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
}
return v_context;
},
@@ -1005,235 +1451,28 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array) || true) { //TODO: remove true
var fields_values = this._build_eval_context();
- final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
+ final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
}
-});
+}));
-openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
- form_template: 'WidgetFrame',
- init: function(view, node) {
- this._super(view, node);
- this.columns = parseInt(node.attrs.col || 4, 10);
- this.x = 0;
- this.y = 0;
- this.table = [];
- this.add_row();
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "newline") {
- this.add_row();
- } else {
- this.handle_node(n);
- }
- }
- this.set_row_cells_with(this.table[this.table.length - 1]);
- },
- renderElement: function() {
- this._super();
- var self = this;
- _.each(this.table, function(row) {
- _.each(row, function(td) {
- td.$element = self.$element.find('.' + td.element_class);
- td.renderElement();
- });
- });
- },
- start: function() {
- _.each(this.table, function(row) {
- _.each(row, function(td) {
- td.start();
- });
- });
- },
- add_row: function(){
- if (this.table.length) {
- this.set_row_cells_with(this.table[this.table.length - 1]);
- }
- var row = [];
- this.table.push(row);
- this.x = 0;
- this.y += 1;
- return row;
- },
- set_row_cells_with: function(row) {
- var bypass = 0,
- max_width = 100,
- row_length = row.length;
- for (var i = 0; i < row.length; i++) {
- if (row[i].always_invisible) {
- row_length--;
- } else {
- bypass += row[i].width === undefined ? 0 : 1;
- max_width -= row[i].decrease_max_width;
- }
- }
- var size_unit = Math.round(max_width / (this.columns - bypass)),
- colspan_sum = 0;
- for (var i = 0; i < row.length; i++) {
- var w = row[i];
- if (w.always_invisible) {
- continue;
- }
- colspan_sum += w.colspan;
- if (w.width === undefined) {
- var width = (i === row_length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan);
- max_width -= width;
- w.width = width + '%';
- }
- }
- },
- handle_node: function(node) {
- var type = {};
- if (node.tag == 'field') {
- type = this.view.fields_view.fields[node.attrs.name] || {};
- if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') {
- // This way we can retain backward compatibility between addons and old clients
- node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1;
- node.attrs.nolabel = '1';
- }
- }
- var widget = instanciate_widget(this.view.registry.get_any(
- [node.attrs.widget, type.type, node.tag]), this.view, node);
- if (node.tag == 'field') {
- if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
- this.view.default_focus_field = widget;
- }
- if (node.attrs.nolabel != '1') {
- var label = instanciate_widget(this.view.registry.get_object('label'), this.view, node);
- label["for"] = widget;
- this.add_widget(label, widget.colspan + 1);
- }
- }
- this.add_widget(widget);
- },
- add_widget: function(widget, colspan) {
- var current_row = this.table[this.table.length - 1];
- if (!widget.always_invisible) {
- colspan = colspan || widget.colspan;
- if (current_row.length && (this.x + colspan) > this.columns) {
- current_row = this.add_row();
- }
- this.x += widget.colspan;
- }
- current_row.push(widget);
- return widget;
- }
-});
-
-openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
- form_template: 'WidgetGroup'
-}),
-
-openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
- form_template: 'WidgetNotebook',
- init: function(view, node) {
- this._super(view, node);
- this.pages = [];
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "page") {
- var page = instanciate_widget(this.view.registry.get_object('notebookpage'),
- this.view, n, this, this.pages.length);
- this.pages.push(page);
- }
- }
- },
- renderElement: function() {
- this._super();
- var self = this;
- _.each(this.pages, function(page) {
- page.$element = self.$element.find('.' + page.element_class);
- page.renderElement();
- });
- },
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.$element.find('> ul > li').each(function (index, tab_li) {
- var page = self.pages[index],
- id = _.uniqueId(self.element_name + '-');
- page.element_id = id;
- $(tab_li).find('a').attr('href', '#' + id);
- });
- this.$element.find('> div').each(function (index, page) {
- page.id = self.pages[index].element_id;
- });
- this.$element.tabs();
- this.view.on_button_new.add_first(this.do_select_first_visible_tab);
- if (openerp.connection.debug) {
- this.do_attach_tooltip(this, this.$element.find('ul:first'), {
- gravity: 's'
- });
- }
- _.each(this.pages, function(page) {
- page.start();
- });
- },
- do_select_first_visible_tab: function() {
- for (var i = 0; i < this.pages.length; i++) {
- var page = this.pages[i];
- if (page.invisible === false) {
- this.$element.tabs('select', page.index);
- break;
- }
- }
- }
-});
-
-openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
- form_template: 'WidgetNotebookPage',
- init: function(view, node, notebook, index) {
- this.notebook = notebook;
- this.index = index;
- this.element_name = 'page_' + index;
- this._super(view, node);
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$element_tab = this.notebook.$element.find(
- '> ul > li:eq(' + this.index + ')');
- },
- update_dom: function() {
- if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
- this.notebook.do_select_first_visible_tab();
- }
- this.$element_tab.toggle(!this.invisible);
- this.$element.toggle(!this.invisible);
- }
-});
-
-openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
- form_template: 'WidgetSeparator',
- init: function(view, node) {
- this._super(view, node);
- this.orientation = node.attrs.orientation || 'horizontal';
- if (this.orientation === 'vertical') {
- this.width = '1';
- }
- this.classname += '_' + this.orientation;
- }
-});
-
-openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
- form_template: 'WidgetButton',
+instance.web.form.WidgetButton = instance.web.form.Widget.extend({
+ template: 'WidgetButton',
init: function(view, node) {
this._super(view, node);
this.force_disabled = false;
- if (this.string) {
- // We don't have button key bindings in the webclient
- this.string = this.string.replace(/_/g, '');
- }
- if (node.attrs.default_focus == '1') {
+ this.string = (this.node.attrs.string || '').replace(/_/g, '');
+ if (this.node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
}
+ this.view.on('view_content_has_changed', this, this.check_disable);
},
start: function() {
this._super.apply(this, arguments);
- this.$element.find("button").click(this.on_click);
- if (this.help || openerp.connection.debug) {
+ this.$element.click(this.on_click);
+ if (this.node.attrs.help || instance.connection.debug) {
this.do_attach_tooltip();
}
},
@@ -1251,7 +1490,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
var exec_action = function() {
if (self.node.attrs.confirm) {
var def = $.Deferred();
- var dialog = openerp.web.dialog($('<div>' + self.node.attrs.confirm + '</div>'), {
+ var dialog = instance.web.dialog($('<div/>').text(self.node.attrs.confirm), {
title: _t('Confirm'),
modal: true,
buttons: [
@@ -1275,6 +1514,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
}
};
if (!this.node.attrs.special) {
+ this.view.force_dirty = true;
return this.view.recursive_save().pipe(exec_action);
} else {
return exec_action();
@@ -1285,7 +1525,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
var context = this.node.attrs.context;
if (context && context.__ref) {
- context = new openerp.web.CompoundContext(context);
+ context = new instance.web.CompoundContext(context);
context.set_eval_context(this._build_eval_context());
}
@@ -1295,81 +1535,57 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
self.view.reload();
});
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.check_disable();
- },
check_disable: function() {
- var disabled = (this.readonly || this.force_disabled || !this.view.is_interactible_record());
- this.$element.find('button').prop('disabled', disabled);
- this.$element.find("button").css('color', disabled ? 'grey' : '');
+ var disabled = (this.force_disabled || !this.view.is_interactible_record());
+ this.$element.prop('disabled', disabled);
+ this.$element.css('color', disabled ? 'grey' : '');
}
});
-openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
- form_template: 'WidgetLabel',
- init: function(view, node) {
- this.element_name = 'label_' + node.attrs.name;
-
- this._super(view, node);
-
- if (this.node.tag == 'label' && !this.string && this.node.children.length) {
- this.string = this.node.children[0];
- this.align = 'left';
- }
-
- if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
- this.form_template = "WidgetParagraph";
- this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
- // Widgets default to right-aligned, but paragraph defaults to
- // left-aligned
- if (isNaN(parseFloat(this.node.attrs.align))) {
- this.align = 'left';
- }
-
- this.multilines = this.string && _.str.lines(this.string).length > 1;
- } else {
- this.colspan = 1;
- this.width = '1%';
- this.decrease_max_width = 1;
- this.nowrap = true;
- }
- },
- renderElement: function() {
- var rendered;
- if (this['for'] && this.type !== 'label') {
- rendered = QWeb.render(this.form_template, {widget: this['for']});
- } else {
- // Actual label widgets should not have a false and have type label
- rendered = QWeb.render(this.form_template, {widget: this});
- }
- this.$element.html(rendered);
- },
- start: function() {
- this._super();
- var self = this;
- if (this['for'] && (this['for'].help || openerp.connection.debug)) {
- this.do_attach_tooltip(self['for']);
- }
- this.$element.find("label").dblclick(function() {
- var widget = self['for'] || self;
- openerp.log(widget.element_class , widget);
- window.w = widget;
- });
- }
-});
+/**
+ * Interface implemented by the form view or any other object
+ * able to provide the features necessary for the fields to work.
+ *
+ * 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.
+ */
+instance.web.form.FieldManagerInterface = {
+ /**
+ * Must return the asked field as in fields_get.
+ */
+ get_field: function(field_name) {},
+ /**
+ * Called by the field when the translate button is clicked.
+ */
+ open_translate_dialog: function(field) {},
+ /**
+ * Returns true when the view is in create mode.
+ */
+ is_create_mode: function() {},
+};
/**
* Interface to be implemented by fields.
*
- * Novajs Attributes:
- * - ...
- *
- * Novajs Events:
- * - ...
+ * Properties:
+ * - readonly: boolean. If set to true the field should appear in readonly mode.
+ * - 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.
+ * Events:
+ * - changed_value: triggered to inform the view to check on_changes
*
*/
-openerp.web.form.FieldInterface = {
+instance.web.form.FieldInterface = {
+ /**
+ * Constructor takes 2 arguments:
+ * - field_manager: Implements FieldManagerInterface
+ * - node: the "<field>" node in json form
+ */
+ init: function(field_manager, node) {},
/**
* Called by the form view to indicate the value of the field.
*
@@ -1386,7 +1602,7 @@ openerp.web.form.FieldInterface = {
* 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.
*/
- set_value: function(value) {},
+ set_value: function(value_) {},
/**
* Get the current value of the widget.
*
@@ -1405,221 +1621,307 @@ openerp.web.form.FieldInterface = {
* syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
*/
get_value: function() {},
+ /**
+ * Inform the current object of the id it should use to match a html <label> that exists somewhere in the
+ * view.
+ */
+ set_input_id: function(id) {},
+ /**
+ * Returns true if is_syntax_valid() returns true and the value is semantically
+ * valid too according to the semantic restrictions applied to the field.
+ */
+ is_valid: function() {},
+ /**
+ * Returns true if the field holds a value which is syntaxically correct, ignoring
+ * the potential semantic restrictions applied to the field.
+ */
+ is_syntax_valid: function() {},
+ /**
+ * Must set the focus on the field.
+ */
+ focus: function() {},
};
/**
- * Abstract class for classes implementing FieldInterface. Should be renamed to AbstractField some
- * day.
+ * Abstract class for classes implementing FieldInterface.
+ *
+ * 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.
+ * - 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.
+ *
*/
-openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
+instance.web.form.AbstractField = instance.web.form.Widget.extend(/** @lends instance.web.form.AbstractField# */{
/**
- * @constructs openerp.web.form.Field
- * @extends openerp.web.form.Widget
+ * @constructs instance.web.form.AbstractField
+ * @extends instance.web.form.Widget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this.name = node.attrs.name;
- this.value = undefined;
- view.fields[this.name] = this;
- view.fields_order.push(this.name);
- this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type;
- this.element_name = "field_" + this.name + "_" + this.type;
-
- this._super(view, node);
-
- if (node.attrs.nolabel != '1' && this.colspan > 1) {
- this.colspan--;
- }
- this.field = view.fields_view.fields[node.attrs.name] || {};
- this.string = node.attrs.string || this.field.string;
- this.help = node.attrs.help || this.field.help;
- this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1';
- this.readonly = this.modifiers['readonly'] === true;
- this.required = this.modifiers['required'] === true;
- this.invalid = this.dirty = false;
-
- this.classname = 'oe_form_field_' + this.type;
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.field_manager = field_manager;
+ this.name = this.node.attrs.name;
+ this.set({'value': false});
+ this.field = this.field_manager.get_field(this.name);
+ this.set({required: this.modifiers['required'] === true});
+
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "force_readonly"
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.on("change:force_readonly", this, test_effective_readonly);
+ _.bind(test_effective_readonly, this)();
+
+ this.on("change:value", this, function() {
+ if (! this._inhibit_on_change)
+ this.trigger('changed_value');
+ this._check_css_flags();
+ });
},
start: function() {
this._super.apply(this, arguments);
if (this.field.translate) {
- this.view.translatable_fields.push(this);
this.$element.addClass('oe_form_field_translatable');
- this.$element.find('.oe_field_translate').click(this.on_translate);
+ this.$element.find('.oe_field_translate').click(_.bind(function() {
+ this.field_manager.open_translate_dialog(this);
+ }, this));
}
- if (this.nolabel && openerp.connection.debug) {
+ if (instance.connection.debug) {
this.do_attach_tooltip(this, this.$element);
}
+ if (!this.disable_utility_classes) {
+ var set_required = function() {
+ this.$element.toggleClass('oe_form_required', this.get("required"));
+ };
+ this.on("change:required", this, set_required);
+ _.bind(set_required, this)();
+ }
},
- set_value: function(value) {
- this.value = value;
- this.invalid = false;
- this.update_dom();
- this.on_value_changed();
- },
- set_value_from_ui: function() {
- this.on_value_changed();
- },
- on_value_changed: function() {
- },
- on_translate: function() {
- this.view.open_translate_dialog(this);
+ set_value: function(value_) {
+ this._inhibit_on_change = true;
+ this.set({'value': value_});
+ this._inhibit_on_change = false;
},
get_value: function() {
- return this.value;
+ return this.get('value');
},
is_valid: function() {
- return !this.invalid;
+ return this.is_syntax_valid() && (! this.get('required') || ! this.is_false());
},
- is_dirty: function() {
- return this.dirty && !this.readonly;
+ is_syntax_valid: function() {
+ return true;
},
- get_on_change_value: function() {
- return this.get_value();
+ /**
+ * Method useful to implement to ease validity testing. Must return true if the current
+ * value is similar to false in OpenERP.
+ */
+ is_false: function() {
+ return this.get('value') === false;
},
- update_dom: function(show_invalid) {
- this._super.apply(this, arguments);
+ _check_css_flags: function(show_invalid) {
if (this.field.translate) {
- this.$element.find('.oe_field_translate').toggle(!!this.view.datarecord.id);
+ this.$element.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode());
}
if (!this.disable_utility_classes) {
- this.$element.toggleClass('disabled', this.readonly);
- this.$element.toggleClass('required', this.required);
- if (show_invalid) {
- this.$element.toggleClass('invalid', !this.is_valid());
+ if (this.field_manager.get('display_invalid_fields')) {
+ this.$element.toggleClass('oe_form_invalid', !this.is_valid());
}
}
},
- on_ui_change: function() {
- this.dirty = true;
- this.validate();
- if (this.is_valid()) {
- this.set_value_from_ui();
- this.view.do_onchange(this);
- this.view.on_form_changed(true);
- this.view.do_notify_change();
- } else {
- this.update_dom(true);
- }
- },
- validate: function() {
- this.invalid = false;
+ focus: function() {
},
- focus: function($element) {
- if ($element) {
- setTimeout(function() {
- $element.focus();
- }, 50);
- }
- },
- reset: function() {
- this.dirty = false;
+ /**
+ * Utility method to focus an element, but only after a small amount of time.
+ */
+ delay_focus: function($elem) {
+ setTimeout(function() {
+ $elem.focus();
+ }, 50);
},
+ /**
+ * Utility method to get the widget options defined in the field xml description.
+ */
get_definition_options: function() {
if (!this.definition_options) {
var str = this.node.attrs.options || '{}';
this.definition_options = JSON.parse(str);
}
return this.definition_options;
- }
+ },
+ set_input_id: function(id) {
+ this.id_for_label = id;
+ },
});
-openerp.web.form.FieldChar = openerp.web.form.Field.extend({
- form_template: 'FieldChar',
- init: function (view, node) {
- this._super(view, node);
- this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
- },
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+instance.web.form.ReinitializeFieldMixin = {
+ /**
+ * Default implementation of start(), use it or call explicitly initialize_field().
+ */
start: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').change(this.on_ui_change);
+ this._super();
+ this.initialize_field();
},
- set_value: function(value) {
- this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('input').val(show_value);
- return show_value;
+ initialize_field: function() {
+ this.on("change:effective_readonly", this, function() {
+ this.destroy_content();
+ this.renderElement();
+ this.initialize_content();
+ this.render_value();
+ });
+ this.initialize_content();
+ this.render_value();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', this.readonly);
+ /**
+ * 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().
+ */
+ render_value: function() {},
+};
+
+instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldChar',
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
- set_value_from_ui: function() {
- this.value = openerp.web.parse_value(this.$element.find('input').val(), this);
- this._super();
+ initialize_content: function() {
+ var self = this;
+ this.$element.find('input').change(function() {
+ self.set({'value': instance.web.parse_value(self.$element.find('input').val(), self)});
+ });
},
- validate: function() {
- this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('input').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
+ },
+ render_value: function() {
+ var show_value = instance.web.format_value(this.get('value'), this, '');
+ if (!this.get("effective_readonly")) {
+ this.$element.find('input').val(show_value);
+ } else {
+ if (this.password) {
+ show_value = new Array(show_value.length + 1).join('*');
+ }
+ this.$element.text(show_value);
}
},
- focus: function($element) {
- this._super($element || this.$element.find('input:first'));
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ try {
+ var value_ = instance.web.parse_value(this.$element.find('input').val(), this, '');
+ return true;
+ } catch(e) {
+ return false;
+ }
+ }
+ return true;
+ },
+ is_false: function() {
+ return this.get('value') === '';
+ },
+ focus: function() {
+ this.delay_focus(this.$element.find('input:first'));
}
-});
+}));
-openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', true);
- }
+instance.web.form.FieldID = instance.web.form.FieldChar.extend({
+
});
-openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
- form_template: 'FieldEmail',
- start: function() {
- this._super.apply(this, arguments);
+instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({
+ template: 'FieldEmail',
+ initialize_content: function() {
+ this._super();
this.$element.find('button').click(this.on_button_clicked);
},
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ this.$element.find('a')
+ .attr('href', 'mailto:' + this.get('value'))
+ .text(this.get('value'));
+ }
+ },
on_button_clicked: function() {
- if (!this.value || !this.is_valid()) {
+ if (!this.get('value') || !this.is_syntax_valid()) {
this.do_warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
- location.href = 'mailto:' + this.value;
+ location.href = 'mailto:' + this.get('value');
}
}
});
-openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
- form_template: 'FieldUrl',
- start: function() {
- this._super.apply(this, arguments);
+instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
+ template: 'FieldUrl',
+ initialize_content: function() {
+ this._super();
this.$element.find('button').click(this.on_button_clicked);
},
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ var tmp = this.get('value');
+ var s = /(\w+):(.+)/.exec(tmp);
+ if (!s) {
+ tmp = "http://" + this.get('value');
+ }
+ this.$element.find('a').attr('href', tmp).text(tmp);
+ }
+ },
on_button_clicked: function() {
- if (!this.value) {
+ if (!this.get('value')) {
this.do_warn("Resource error", "This resource is empty");
} else {
- window.open(this.value);
+ var url = $.trim(this.get('value'));
+ if(/^www\./i.test(url))
+ url = 'http://'+url;
+ window.open(url);
}
}
});
-openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
- init: function (view, node) {
- this._super(view, node);
- if (node.attrs.digits) {
+instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
+ is_field_number: true,
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ this.set({'value': 0});
+ if (this.node.attrs.digits) {
this.digits = py.eval(node.attrs.digits);
} else {
- this.digits = view.fields_view.fields[node.attrs.name].digits;
+ this.digits = this.field.digits;
}
},
- set_value: function(value) {
- if (value === false || value === undefined) {
+ set_value: function(value_) {
+ if (value_ === false || value_ === undefined) {
// As in GTK client, floats default to 0
- value = 0;
+ value_ = 0;
}
- this._super.apply(this, [value]);
+ this._super.apply(this, [value_]);
}
});
-openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
+instance.web.DateTimeWidget = instance.web.OldWidget.extend({
template: "web.datetimepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
@@ -1640,46 +1942,46 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
showButtonPanel: true
});
this.$element.find('img.oe_datepicker_trigger').click(function() {
- if (!self.readonly && !self.picker('widget').is(':visible')) {
- self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
+ if (!self.get("effective_readonly") && !self.picker('widget').is(':visible')) {
+ self.picker('setDate', self.get('value') ? instance.web.auto_str_to_date(self.get('value')) : new Date());
self.$input_picker.show();
self.picker('show');
self.$input_picker.hide();
}
});
this.set_readonly(false);
- this.value = false;
+ this.set({'value': false});
},
picker: function() {
return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments);
},
- on_picker_select: function(text, instance) {
+ on_picker_select: function(text, instance_) {
var date = this.picker('getDate');
this.$input.val(date ? this.format_client(date) : '').change();
},
- set_value: function(value) {
- this.value = value;
- this.$input.val(value ? this.format_client(value) : '');
+ set_value: function(value_) {
+ this.set({'value': value_});
+ this.$input.val(value_ ? this.format_client(value_) : '');
},
get_value: function() {
- return this.value;
+ return this.get('value');
},
- set_value_from_ui: function() {
- var value = this.$input.val() || false;
- this.value = this.parse_client(value);
+ set_value_from_ui_: function() {
+ var value_ = this.$input.val() || false;
+ this.set({'value': this.parse_client(value_)});
},
set_readonly: function(readonly) {
this.readonly = readonly;
this.$input.prop('readonly', this.readonly);
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
- is_valid: function(required) {
- var value = this.$input.val();
- if (value === "") {
- return !required;
+ is_valid_: function() {
+ var value_ = this.$input.val();
+ if (value_ === "") {
+ return true;
} else {
try {
- this.parse_client(value);
+ this.parse_client(value_);
return true;
} catch(e) {
return false;
@@ -1687,99 +1989,120 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
}
},
parse_client: function(v) {
- return openerp.web.parse_value(v, {"widget": this.type_of_date});
+ return instance.web.parse_value(v, {"widget": this.type_of_date});
},
format_client: function(v) {
- return openerp.web.format_value(v, {"widget": this.type_of_date});
+ return instance.web.format_value(v, {"widget": this.type_of_date});
},
on_change: function() {
- if (this.is_valid()) {
- this.set_value_from_ui();
+ if (this.is_valid_()) {
+ this.set_value_from_ui_();
}
}
});
-openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
+instance.web.DateWidget = instance.web.DateTimeWidget.extend({
jqueryui_object: 'datepicker',
type_of_date: "date"
});
-openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
- form_template: "EmptyComponent",
+instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: "EmptyComponent",
build_widget: function() {
- return new openerp.web.DateTimeWidget(this);
+ return new instance.web.DateTimeWidget(this);
},
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.datewidget = this.build_widget();
- this.datewidget.on_change.add_last(this.on_ui_change);
- this.datewidget.appendTo(this.$element);
+ destroy_content: function() {
+ if (this.datewidget) {
+ this.datewidget.destroy();
+ this.datewidget = undefined;
+ }
+ },
+ initialize_content: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget = this.build_widget();
+ this.datewidget.on_change.add_last(_.bind(function() {
+ this.set({'value': this.datewidget.get_value()});
+ }, this));
+ this.datewidget.appendTo(this.$element);
+ }
},
- set_value: function(value) {
- this._super(value);
- this.datewidget.set_value(value);
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
},
- get_value: function() {
- return this.datewidget.get_value();
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget.set_value(this.get('value'));
+ } else {
+ this.$element.text(instance.web.format_value(this.get('value'), this, ''));
+ }
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.datewidget.set_readonly(this.readonly);
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ return this.datewidget.is_valid_();
+ }
+ return true;
},
- validate: function() {
- this.invalid = !this.datewidget.is_valid(this.required);
+ is_false: function() {
+ return this.get('value') === '';
},
- focus: function($element) {
- this._super($element || this.datewidget.$input);
+ focus: function() {
+ if (this.datewidget && this.datewidget.$input)
+ this.delay_focus(this.datewidget.$input);
}
-});
+}));
-openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
+instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({
build_widget: function() {
- return new openerp.web.DateWidget(this);
+ return new instance.web.DateWidget(this);
}
});
-openerp.web.form.FieldText = openerp.web.form.Field.extend({
- form_template: 'FieldText',
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('textarea').change(this.on_ui_change);
+instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldText',
+ initialize_content: function() {
+ this.$textarea = this.$element.find('textarea');
this.resized = false;
+ if (!this.get("effective_readonly")) {
+ this.$textarea.change(_.bind(function() {
+ this.set({'value': instance.web.parse_value(this.$textarea.val(), this)});
+ }, this));
+ } else {
+ this.$textarea.attr('disabled', 'disabled');
+ }
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('textarea').val(show_value);
+ this.render_value();
+ },
+ render_value: function() {
+ var show_value = instance.web.format_value(this.get('value'), this, '');
+ this.$textarea.val(show_value);
if (!this.resized && this.view.options.resize_textareas) {
this.do_resize(this.view.options.resize_textareas);
this.resized = true;
}
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('textarea').prop('readonly', this.readonly);
- },
- set_value_from_ui: function() {
- this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this);
- this._super();
- },
- validate: function() {
- this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('textarea').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ try {
+ var value_ = instance.web.parse_value(this.$textarea.val(), this, '');
+ return true;
+ } catch(e) {
+ return false;
+ }
}
+ return true;
+ },
+ is_false: function() {
+ return this.get('value') === '';
},
focus: function($element) {
- this._super($element || this.$element.find('textarea:first'));
+ this.delay_focus(this.$textarea);
},
do_resize: function(max_height) {
max_height = parseInt(max_height, 10);
- var $input = this.$element.find('textarea'),
+ var $input = this.$textarea,
$div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
new_height;
$div.text($input.val());
@@ -1797,64 +2120,60 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
$div.remove();
$input.height(new_height);
},
- reset: function() {
- this.resized = false;
- }
-});
+}));
-openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
- form_template: 'FieldBoolean',
+instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
+ template: 'FieldBoolean',
start: function() {
- var self = this;
this._super.apply(this, arguments);
- this.$element.find('input').click(self.on_ui_change);
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.$element.find('input')[0].checked = value;
- },
- set_value_from_ui: function() {
- this.value = this.$element.find('input').is(':checked');
- this._super();
+ this.$checkbox = $("input", this.$element);
+ this.$element.click(_.bind(function() {
+ this.set({'value': this.$checkbox.is(':checked')});
+ }, this));
+ var check_readonly = function() {
+ this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ };
+ this.on("change:effective_readonly", this, check_readonly);
+ _.bind(check_readonly, this)();
},
- update_dom: function() {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', this.readonly);
+ this.$checkbox[0].checked = value_;
},
- focus: function($element) {
- this._super($element || this.$element.find('input:first'));
+ focus: function() {
+ this.delay_focus(this.$checkbox);
}
});
-openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
- form_template: 'FieldProgressBar',
+instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
+ template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
- this.$element.find('div').progressbar({
- value: this.value,
- disabled: this.readonly
+ this.$element.progressbar({
+ value: this.get('value'),
+ disabled: this.get("effective_readonly")
});
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- var show_value = Number(value);
+ var show_value = Number(value_);
if (isNaN(show_value)) {
show_value = 0;
}
- var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0');
- this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
+ var formatted_value = instance.web.format_value(show_value, { type : 'float' }, '0');
+ this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
}
});
-openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
+instance.web.form.FieldTextXml = instance.web.form.AbstractField.extend({
// to replace view editor
});
-openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
- form_template: 'FieldSelection',
- init: function(view, node) {
+instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldSelection',
+ init: function(field_manager, node) {
var self = this;
- this._super(view, node);
+ this._super(field_manager, node);
this.values = _.clone(this.field.selection);
_.each(this.values, function(v, i) {
if (v[0] === false && v[1] === '') {
@@ -1863,7 +2182,7 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
});
this.values.unshift([false, '']);
},
- start: function() {
+ initialize_content: function() {
// 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
@@ -1876,9 +2195,10 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
// changing the selected value), takes the action as validating the
// row
var ischanging = false;
- this._super.apply(this, arguments);
this.$element.find('select')
- .change(this.on_ui_change)
+ .change(_.bind(function() {
+ this.set({'value': this.values[this.$element.find('select')[0].selectedIndex][0]});
+ }, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
.keyup(function (e) {
@@ -1887,32 +2207,37 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
ischanging = false;
});
},
- set_value: function(value) {
- value = value === null ? false : value;
- value = value instanceof Array ? value[0] : value;
- this._super(value);
- var index = 0;
- for (var i = 0, ii = this.values.length; i < ii; i++) {
- if (this.values[i][0] === value) index = i;
- }
- this.$element.find('select')[0].selectedIndex = index;
- },
- set_value_from_ui: function() {
- this.value = this.values[this.$element.find('select')[0].selectedIndex][0];
- this._super();
+ set_value: function(value_) {
+ value_ = value_ === null ? false : value_;
+ value_ = value_ instanceof Array ? value_[0] : value_;
+ this._super(value_);
+ this.render_value();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('select').prop('disabled', this.readonly);
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ var index = 0;
+ for (var i = 0, ii = this.values.length; i < ii; i++) {
+ if (this.values[i][0] === this.get('value')) index = i;
+ }
+ this.$element.find('select')[0].selectedIndex = index;
+ } else {
+ var self = this;
+ var option = _(this.values)
+ .detect(function (record) { return record[0] === self.get('value'); });
+ this.$element.text(option ? option[1] : this.values[0][1]);
+ }
},
- validate: function() {
- var value = this.values[this.$element.find('select')[0].selectedIndex];
- this.invalid = !(value && !(this.required && value[0] === false));
+ is_syntax_valid: function() {
+ if (this.get("effective_readonly")) {
+ return true;
+ }
+ var value_ = this.values[this.$element.find('select')[0].selectedIndex];
+ return !! value_;
},
- focus: function($element) {
- this._super($element || this.$element.find('select:first'));
+ focus: function() {
+ this.delay_focus(this.$element.find('select:first'));
}
-});
+}));
// jquery autocomplete tweak to allow html
(function() {
@@ -1921,8 +2246,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
function filter( array, term ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
- return $.grep( array, function(value) {
- return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() );
+ return $.grep( array, function(value_) {
+ return matcher.test( $( "<div>" ).html( value_.label || value_.value || value_ ).text() );
});
}
@@ -1946,114 +2271,88 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
});
})();
-openerp.web.form.dialog = function(content, options) {
+instance.web.form.dialog = function(content, options) {
options = _.extend({
width: '90%',
height: 'auto',
min_width: '800px'
}, options || {});
- var dialog = new openerp.web.Dialog(null, options, content).open();
+ var dialog = new instance.web.Dialog(null, options, content).open();
return dialog.$element;
};
-openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
- form_template: 'FieldMany2One',
- init: function(view, node) {
- this._super(view, node);
+instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: "FieldMany2One",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.limit = 7;
- this.value = null;
- this.cm_id = _.uniqueId('m2o_cm_');
+ this.set({'value': false});
+ this.display_value = {};
this.last_search = [];
- this.tmp_value = undefined;
+ this.floating = false;
+ this.inhibit_on_change = false;
this.orderer = new openerp.web.DropMisordered();
},
start: function() {
this._super();
+ instance.web.form.ReinitializeFieldMixin.start.call(this);
+ this.on("change:value", this, function() {
+ this.floating = false;
+ this.render_value();
+ });
+ },
+ initialize_content: function() {
+ if (!this.get("effective_readonly"))
+ this.render_editable();
+ this.render_value();
+ },
+ render_editable: function() {
var self = this;
this.$input = this.$element.find("input");
+
+ self.$input.tipsy({
+ title: function() {
+ return "No element was selected, you should create or select one from the dropdown list.";
+ },
+ trigger:'manual',
+ fade: true,
+ });
+
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
- this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
-
- // context menu
- var init_context_menu_def = $.Deferred().then(function(e) {
- var rdataset = new openerp.web.DataSet(self, "ir.values", self.build_context());
- rdataset.call("get", ['action', 'client_action_relate',
- [[self.field.relation, false]], false, rdataset.get_context()], false, 0)
- .then(function(result) {
- self.related_entries = result;
-
- var $cmenu = $("#" + self.cm_id);
- $cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
- var bindings = {};
- bindings[self.cm_id + "_search"] = function() {
- if (self.readonly)
- return;
- self._search_create_popup("search");
- };
- bindings[self.cm_id + "_create"] = function() {
- if (self.readonly)
- return;
- self._search_create_popup("form");
- };
- bindings[self.cm_id + "_open"] = function() {
- if (!self.value) {
- return;
- }
- var pop = new openerp.web.form.FormOpenPopup(self.view);
- pop.show_element(
- self.field.relation,
- self.value[0],
- self.build_context(),
- {
- title: _t("Open: ") + (self.string || self.name)
- }
- );
- pop.on_write_completed.add_last(function() {
- self.set_value(self.value[0]);
- });
- };
- _.each(_.range(self.related_entries.length), function(i) {
- bindings[self.cm_id + "_related_" + i] = function() {
- self.open_related(self.related_entries[i]);
- };
- });
- var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true,
- bindings: bindings, itemStyle: {"color": ""},
- onContextMenu: function() {
- if(self.value) {
- $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").removeClass("oe-m2o-disabled-cm");
- } else {
- $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm");
- }
- if (!self.readonly) {
- $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").removeClass("oe-m2o-disabled-cm");
- } else {
- $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").addClass("oe-m2o-disabled-cm");
- }
- return true;
- }, menuStyle: {width: "200px"}
- });
- $.async_when().then(function() {self.$menu_btn.trigger(e);});
+ this.$follow_button = $(".oe-m2o-cm-button", this.$element);
+
+ this.$follow_button.click(function() {
+ if (!self.get('value')) {
+ return;
+ }
+ var pop = new instance.web.form.FormOpenPopup(self.view);
+ pop.show_element(
+ self.field.relation,
+ self.get("value"),
+ self.build_context(),
+ {
+ title: _t("Open: ") + (self.string || self.name)
+ }
+ );
+ pop.on_write_completed.add_last(function() {
+ self.display_value = {};
+ self.render_value();
});
});
- var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()};
- this.$menu_btn.click(ctx_callback);
// some behavior for input
this.$input.keyup(function() {
if (self.$input.val() === "") {
- self._change_int_value(null);
- } else if (self.value === null || (self.value && self.$input.val() !== self.value[1])) {
- self._change_int_value(undefined);
+ self.set({value: false});
+ } else {
+ self.floating = true;
}
});
this.$drop_down.click(function() {
- if (self.readonly)
- return;
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
} else {
- if (self.value) {
+ if (self.get("value") && ! self.floating) {
self.$input.autocomplete("search", "");
} else {
self.$input.autocomplete("search");
@@ -2061,16 +2360,43 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
self.$input.focus();
}
});
+ var tip_def = $.Deferred();
+ var untip_def = $.Deferred();
+ var tip_delay = 200;
+ var tip_duration = 3000;
var anyoneLoosesFocus = function() {
- if (!self.$input.is(":focus") &&
- !self.$input.autocomplete("widget").is(":visible") &&
- !self.value) {
- if (self.value === undefined && self.last_search.length > 0) {
- self._change_int_ext_value(self.last_search[0]);
+ 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 {
+ self.render_value();
+ }
} else {
- self._change_int_ext_value(null);
+ self.set({value: false});
}
}
+ if (! self.get("value")) {
+ tip_def.reject();
+ untip_def.reject();
+ tip_def = $.Deferred();
+ tip_def.then(function() {
+ self.$input.tipsy("show");
+ });
+ setTimeout(function() {
+ tip_def.resolve();
+ untip_def.reject();
+ untip_def = $.Deferred();
+ untip_def.then(function() {
+ self.$input.tipsy("hide");
+ });
+ setTimeout(function() {untip_def.resolve();}, tip_duration);
+ }, tip_delay);
+ } else {
+ tip_def.reject();
+ }
};
this.$input.focusout(anyoneLoosesFocus);
@@ -2082,9 +2408,11 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
isSelecting = true;
var item = ui.item;
if (item.id) {
- self._change_int_value([item.id, item.name]);
+ self.display_value = {};
+ self.display_value["" + item.id] = item.name;
+ self.set({value: item.id});
} else if (item.action) {
- self._change_int_value(undefined);
+ self.floating = true;
item.action();
return false;
}
@@ -2112,7 +2440,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
var search_val = request.term;
var self = this;
- var dataset = new openerp.web.DataSet(this, this.field.relation, self.build_context());
+ var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
this.orderer.add(dataset.name_search(
search_val, self.build_domain(), 'ilike', this.limit + 1)).then(function(data) {
@@ -2133,7 +2461,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
values.push({label: _t("<em>   Search More...</em>"), action: function() {
dataset.name_search(search_val, self.build_domain(), 'ilike'
, false, function(data) {
- self._change_int_value(null);
self._search_create_popup("search", data);
});
}});
@@ -2142,7 +2469,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
var raw_result = _(data.result).map(function(x) {return x[1];});
if (search_val.length > 0 &&
!_.include(raw_result, search_val) &&
- (!self.value || search_val !== self.value[1])) {
+ (!self.get("value") || self.floating)) {
values.push({label: _.str.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
$('<span />').text(search_val).html()), action: function() {
self._quick_create(search_val);
@@ -2150,7 +2477,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
}
// create...
values.push({label: _t("<em>   Create and Edit...</em>"), action: function() {
- self._change_int_value(null);
self._search_create_popup("form", undefined, {"default_name": search_val});
}});
@@ -2160,13 +2486,14 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
_quick_create: function(name) {
var self = this;
var slow_create = function () {
- self._change_int_value(null);
self._search_create_popup("form", undefined, {"default_name": name});
};
if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
- new openerp.web.DataSet(this, this.field.relation, self.build_context())
+ new instance.web.DataSet(this, this.field.relation, self.build_context())
.name_create(name, function(data) {
- self._change_int_ext_value(data);
+ self.display_value = {};
+ self.display_value["" + data[0]] = data[1];
+ self.set({value: data[0]});
}).fail(function(error, event) {
event.preventDefault();
slow_create();
@@ -2177,7 +2504,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
// all search/create popup handling
_search_create_popup: function(view, ids, context) {
var self = this;
- var pop = new openerp.web.form.SelectCreatePopup(this);
+ var pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element(
self.field.relation,
{
@@ -2187,100 +2514,71 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
disable_multiple_selection: true
},
self.build_domain(),
- new openerp.web.CompoundContext(self.build_context(), context || {})
+ new instance.web.CompoundContext(self.build_context(), context || {})
);
pop.on_select_elements.add(function(element_ids) {
- new openerp.web.DataSet(self, self.field.relation, self.build_context())
- .name_get([element_ids[0]], function(data) {
- self._change_int_ext_value(data[0]);
- });
+ self.set({value: element_ids[0]});
});
},
- _change_int_ext_value: function(value) {
- this._change_int_value(value);
- this.$input.val(this.value ? this.value[1] : "");
- },
- _change_int_value: function(value) {
- this.value = value;
- var back_orig_value = this.original_value;
- if (this.value === null || this.value) {
- this.original_value = this.value;
+ render_value: function(no_recurse) {
+ var self = this;
+ if (! this.get("value")) {
+ this.display_string("");
+ return;
}
- if (back_orig_value === undefined) { // first use after a set_value()
+ var display = this.display_value["" + this.get("value")];
+ if (display) {
+ this.display_string(display);
return;
}
- if (this.value !== undefined && ((back_orig_value ? back_orig_value[0] : null)
- !== (this.value ? this.value[0] : null))) {
- this.on_ui_change();
+ if (! no_recurse) {
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ dataset.name_get([self.get("value")], function(data) {
+ self.display_value["" + self.get("value")] = data[0][1];
+ self.render_value(true);
+ });
}
},
- set_value: function(value) {
- value = value || null;
- this.invalid = false;
+ display_string: function(str) {
var self = this;
- this.tmp_value = value;
- self.update_dom();
- self.on_value_changed();
- var real_set_value = function(rval) {
- self.tmp_value = undefined;
- self.value = rval;
- self.original_value = undefined;
- self._change_int_ext_value(rval);
- };
- if (value && !(value instanceof Array)) {
- // name_get in a m2o does not use the context of the field
- new openerp.web.DataSet(this, this.field.relation, self.view.dataset.get_context())
- .name_get([value], function(data) {
- real_set_value(data[0]);
- }).fail(function() {self.tmp_value = undefined;});
+ if (!this.get("effective_readonly")) {
+ this.$input.val(str);
} else {
- $.async_when().then(function() {real_set_value(value);});
- }
- },
- get_value: function() {
- if (this.tmp_value !== undefined) {
- if (this.tmp_value instanceof Array) {
- return this.tmp_value[0];
- }
- return this.tmp_value ? this.tmp_value : false;
- }
- if (this.value === undefined)
- return this.original_value ? this.original_value[0] : false;
- return this.value ? this.value[0] : false;
- },
- validate: function() {
- this.invalid = false;
- var val = this.tmp_value !== undefined ? this.tmp_value : this.value;
- if (val === null) {
- this.invalid = this.required;
+ this.$element.find('a')
+ .unbind('click')
+ .text(str)
+ .click(function () {
+ self.do_action({
+ type: 'ir.actions.act_window',
+ res_model: self.field.relation,
+ res_id: self.get("value"),
+ context: self.build_context(),
+ views: [[false, 'form']],
+ target: 'current'
+ });
+ return false;
+ });
}
},
- open_related: function(related) {
+ set_value: function(value_) {
var self = this;
- if (!self.value)
- return;
- var additional_context = {
- active_id: self.value[0],
- active_ids: [self.value[0]],
- active_model: self.field.relation
- };
- self.rpc("/web/action/load", {
- action_id: related[2].id,
- context: additional_context
- }, function(result) {
- result.result.context = _.extend(result.result.context || {}, additional_context);
- self.do_action(result.result);
- });
+ if (value_ instanceof Array) {
+ this.display_value = {};
+ this.display_value["" + value_[0]] = value_[1];
+ value_ = value_[0];
+ }
+ value_ = value_ || false;
+ this.inhibit_on_change = true;
+ this._super(value_);
+ this.inhibit_on_change = false;
},
- focus: function ($element) {
- this._super($element || this.$input);
+ is_false: function() {
+ return ! this.get("value");
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$input.prop('readonly', this.readonly);
+ focus: function () {
+ this.delay_focus(this.$input);
}
-});
-openerp.web.check_interface(openerp.web.form.FieldMany2One, openerp.web.form.FieldInterface);
+}));
/*
# Values: (0, 0, { fields }) create
@@ -2327,24 +2625,24 @@ var commands = {
return [6, false, ids];
}
};
-openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
- form_template: 'FieldOne2Many',
+instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
- init: function(view, node) {
- this._super(view, node);
+ disable_utility_classes: true,
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ lazy_build_o2m_kanban_view();
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
this.form_last_update = $.Deferred();
this.init_form_last_update = this.form_last_update;
- this.disable_utility_classes = true;
},
start: function() {
this._super.apply(this, arguments);
var self = this;
- this.dataset = new openerp.web.form.One2ManyDataSet(this, this.field.relation);
+ this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation);
this.dataset.o2m = this;
this.dataset.parent_view = this.view;
this.dataset.child_name = this.name;
@@ -2356,16 +2654,23 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
this.is_setted.then(function() {
self.load_views();
});
+ 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();
+ });
+ });
+ });
+ });
},
trigger_on_change: function() {
var tmp = this.doing_on_change;
this.doing_on_change = true;
- this.on_ui_change();
+ this.trigger('changed_value');
this.doing_on_change = tmp;
},
- is_readonly: function() {
- return this.readonly || this.force_readonly;
- },
load_views: function() {
var self = this;
@@ -2376,21 +2681,21 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
var view = {
view_id: false,
view_type: mode == "tree" ? "list" : mode,
- options: { sidebar : false }
+ options: {}
};
if (self.field.views && self.field.views[mode]) {
view.embedded_view = self.field.views[mode];
}
if(view.view_type === "list") {
view.options.selectable = self.multi_selection;
- if (self.is_readonly()) {
+ if (self.get("effective_readonly")) {
view.options.addable = null;
view.options.deletable = null;
view.options.isClarkGable = false;
}
} else if (view.view_type === "form") {
- if (self.is_readonly()) {
- view.view_type = 'page';
+ if (self.get("effective_readonly")) {
+ view.view_type = 'form';
}
view.options.not_interactible_on_create = true;
}
@@ -2398,12 +2703,14 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
});
this.views = views;
- this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {});
+ this.viewmanager = new instance.web.ViewManager(this, this.dataset, views, {
+ $sidebar: false,
+ });
this.viewmanager.template = 'One2Many.viewmanager';
- this.viewmanager.registry = openerp.web.views.extend({
- list: 'openerp.web.form.One2ManyListView',
- form: 'openerp.web.form.One2ManyFormView',
- page: 'openerp.web.PageView'
+ this.viewmanager.registry = instance.web.views.extend({
+ list: 'instance.web.form.One2ManyListView',
+ form: 'instance.web.form.One2ManyFormView',
+ kanban: 'instance.web.form.One2ManyKanbanView',
});
var once = $.Deferred().then(function() {
self.init_form_last_update.resolve();
@@ -2412,12 +2719,12 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
self.initial_is_loaded.resolve();
});
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
+ controller.o2m = self;
if (view_type == "list") {
- controller.o2m = self;
- if (self.is_readonly())
+ if (self.get("effective_readonly"))
controller.set_editable(false);
- } else if (view_type == "form" || view_type == 'page') {
- if (view_type == 'page' || self.is_readonly()) {
+ } else if (view_type === "form") {
+ if (self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$element).children().remove();
}
controller.on_record_loaded.add_last(function() {
@@ -2451,7 +2758,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
var view = self.viewmanager.views[active_view].controller;
if(active_view === "list") {
return view.reload_content();
- } else if (active_view === "form" || active_view === 'page') {
+ } else if (active_view === "form") {
if (self.dataset.index === null && self.dataset.ids.length >= 1) {
self.dataset.index = 0;
}
@@ -2460,18 +2767,18 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
};
self.form_last_update = self.form_last_update.pipe(act, act);
return self.form_last_update;
- } else if (active_view === "graph") {
+ } else if (view.do_search) {
return view.do_search(self.build_domain(), self.dataset.get_context(), []);
}
}, undefined);
},
- set_value: function(value) {
- value = value || [];
+ set_value: function(value_) {
+ value_ = value_ || [];
var self = this;
this.dataset.reset_ids([]);
- if(value.length >= 1 && value[0] instanceof Array) {
+ if(value_.length >= 1 && value_[0] instanceof Array) {
var ids = [];
- _.each(value, function(command) {
+ _.each(value_, function(command) {
var obj = {values: command[2]};
switch (command[0]) {
case commands.CREATE:
@@ -2500,10 +2807,10 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
});
this._super(ids);
this.dataset.set_ids(ids);
- } else if (value.length >= 1 && typeof(value[0]) === "object") {
+ } else if (value_.length >= 1 && typeof(value_[0]) === "object") {
var ids = [];
this.dataset.delete_all = true;
- _.each(value, function(command) {
+ _.each(value_, function(command) {
var obj = {values: command};
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
obj.defaults = {};
@@ -2514,8 +2821,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
this._super(ids);
this.dataset.set_ids(ids);
} else {
- this._super(value);
- this.dataset.reset_ids(value);
+ this._super(value_);
+ this.dataset.reset_ids(value_);
}
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
@@ -2572,61 +2879,37 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
return false;
}, this));
},
- is_valid: function() {
- this.validate();
- return this._super();
- },
- validate: function() {
- this.invalid = false;
+ is_syntax_valid: function() {
if (!this.viewmanager.views[this.viewmanager.active_view])
- return;
+ return true;
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") {
for (var f in view.fields) {
f = view.fields[f];
if (!f.is_valid()) {
- this.invalid = true;
- return;
+ return false;
}
}
}
+ return true;
},
- is_dirty: function() {
- this.save_any_view();
- return this._super();
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.viewmanager) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.viewmanager.destroy();
- return $.when(self.load_views()).then(function() {
- self.reload_current_view();
- });
- });
- }
- }
- }
});
-openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({
+instance.web.form.One2ManyDataSet = instance.web.BufferedDataSet.extend({
get_context: function() {
this.context = this.o2m.build_context([this.o2m.name]);
return this.context;
}
});
-openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
+instance.web.form.One2ManyListView = instance.web.ListView.extend({
_template: 'One2Many.listview',
do_add_record: function () {
if (this.options.editable) {
this._super.apply(this, arguments);
} else {
var self = this;
- var pop = new openerp.web.form.SelectCreatePopup(this);
+ var pop = new instance.web.form.SelectCreatePopup(this);
pop.on_default_get.add(self.dataset.on_default_get);
pop.select_element(
self.o2m.field.relation,
@@ -2657,7 +2940,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
},
do_activate_record: function(index, id) {
var self = this;
- var pop = new openerp.web.form.FormOpenPopup(self.o2m.view);
+ var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.name,
auto_write: false,
@@ -2668,7 +2951,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
},
form_view_options: {'not_interactible_on_create':true},
- readonly: self.o2m.is_readonly()
+ readonly: self.o2m.get("effective_readonly")
});
pop.on_write.add(function(id, data) {
self.o2m.dataset.write(id, data, {}, function(r) {
@@ -2683,12 +2966,12 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
}
});
-openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
+instance.web.form.One2ManyFormView = instance.web.FormView.extend({
form_template: 'One2Many.formview',
on_loaded: function(data) {
this._super(data);
var self = this;
- this.$form_header.find('button.oe_form_button_create').click(function() {
+ this.$buttons.find('button.oe_form_button_create').click(function() {
self.do_save().then(self.on_button_new);
});
},
@@ -2701,12 +2984,43 @@ openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
}
});
-openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
- form_template: 'FieldMany2Many',
+var lazy_build_o2m_kanban_view = function() {
+if (! instance.web_kanban || instance.web.form.One2ManyKanbanView)
+ return;
+instance.web.form.One2ManyKanbanView = instance.web_kanban.KanbanView.extend({
+ open_record: function(id) {
+ var self = this;
+ var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
+ pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
+ title: _t("Open: ") + self.name,
+ auto_write: false,
+ 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"),
+ });
+ pop.on_write.add(function(id, data) {
+ self.o2m.dataset.write(id, data, {}, function(r) {
+ self.o2m.reload_current_view();
+ });
+ });
+
+ },
+});
+}
+
+/*
+ * TODO niv: clean those deferred stuff, it could be better
+ */
+instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
- init: function(view, node) {
- this._super(view, node);
- this.list_id = _.uniqueId("many2many");
+ disable_utility_classes: true,
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
@@ -2716,43 +3030,44 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
var self = this;
- this.dataset = new openerp.web.form.Many2ManyDataSet(this, this.field.relation);
+ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
this.dataset.on_unlink.add_last(function(ids) {
- self.on_ui_change();
+ self.dataset_changed();
});
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.list_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];
+ 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);
+ this._super(value_);
+ this.dataset.set_ids(value_);
var self = this;
self.reload_content();
this.is_setted.resolve();
},
- get_value: function() {
- return [commands.replace_with(this.dataset.ids)];
- },
- validate: function() {
- this.invalid = false;
- },
- is_readonly: function() {
- return this.readonly || this.force_readonly;
- },
load_view: function() {
var self = this;
- this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, {
- 'addable': self.is_readonly() ? null : _t("Add"),
- 'deletable': self.is_readonly() ? false : true,
+ this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, {
+ 'addable': self.get("effective_readonly") ? null : _t("Add"),
+ 'deletable': self.get("effective_readonly") ? false : true,
'selectable': self.multi_selection,
- 'isClarkGable': self.is_readonly() ? false : true
+ 'isClarkGable': self.get("effective_readonly") ? false : true
});
var embedded = (this.field.views || {}).tree;
if (embedded) {
@@ -2765,7 +3080,7 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo($("#" + self.list_id));
+ self.list_view.appendTo(self.$element);
});
return loaded;
},
@@ -2775,24 +3090,12 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
return self.list_view.reload_content();
});
},
- update_dom: function() {
- this._super.apply(this, arguments);
- var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.list_view) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.list_view.destroy();
- return $.when(self.load_view()).then(function() {
- self.reload_content();
- });
- });
- }
- }
- }
+ dataset_changed: function() {
+ this.set({'value': [commands.replace_with(this.dataset.ids)]});
+ },
});
-openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
+instance.web.form.Many2ManyDataSet = instance.web.DataSetStatic.extend({
get_context: function() {
this.context = this.m2m.build_context();
return this.context;
@@ -2801,25 +3104,25 @@ openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
/**
* @class
- * @extends openerp.web.ListView
+ * @extends instance.web.ListView
*/
-openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{
+instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends instance.web.form.Many2ManyListView# */{
do_add_record: function () {
- var pop = new openerp.web.form.SelectCreatePopup(this);
+ var pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element(
this.model,
{
title: _t("Add: ") + this.name
},
- new openerp.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
+ new instance.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
this.m2m_field.build_context()
);
var self = this;
pop.on_select_elements.add(function(element_ids) {
- _.each(element_ids, function(element_id) {
- if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
- self.dataset.set_ids([].concat(self.dataset.ids, [element_id]));
- self.m2m_field.on_ui_change();
+ _.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.m2m_field.dataset_changed();
self.reload_content();
}
});
@@ -2827,10 +3130,10 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open
},
do_activate_record: function(index, id) {
var self = this;
- var pop = new openerp.web.form.FormOpenPopup(this);
+ var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
title: _t("Open: ") + this.name,
- readonly: this.getParent().is_readonly()
+ readonly: this.getParent().get("effective_readonly")
});
pop.on_write_completed.add_last(function() {
self.reload_content();
@@ -2840,9 +3143,9 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open
/**
* @class
- * @extends openerp.web.OldWidget
+ * @extends instance.web.OldWidget
*/
-openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.SelectCreatePopup# */{
+instance.web.form.SelectCreatePopup = instance.web.OldWidget.extend(/** @lends instance.web.form.SelectCreatePopup# */{
template: "SelectCreatePopup",
/**
* options:
@@ -2868,7 +3171,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.initial_ids = this.options.initial_ids;
this.created_elements = [];
this.renderElement();
- openerp.web.form.dialog(this.$element, {
+ instance.web.form.dialog(this.$element, {
close: function() {
self.check_exit();
},
@@ -2879,7 +3182,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
start: function() {
this._super();
var self = this;
- this.dataset = new openerp.web.ProxyDataSet(this, this.model,
+ this.dataset = new instance.web.ProxyDataSet(this, this.model,
this.context);
this.dataset.create_function = function() {
return self.options.create_function.apply(null, arguments).then(function(r) {
@@ -2899,10 +3202,10 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
contexts: [this.context]
}, function (results) {
var search_defaults = {};
- _.each(results.context, function (value, key) {
+ _.each(results.context, function (value_, key) {
var match = /^search_default_(.*)$/.exec(key);
if (match) {
- search_defaults[match[1]] = value;
+ search_defaults[match[1]] = value_;
}
});
self.setup_search_view(search_defaults);
@@ -2920,7 +3223,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
if (this.searchview) {
this.searchview.destroy();
}
- this.searchview = new openerp.web.SearchView(this,
+ this.searchview = new instance.web.SearchView(this,
this.dataset, false, search_defaults);
this.searchview.on_search.add(function(domains, contexts, groupbys) {
if (self.initial_ids) {
@@ -2932,13 +3235,13 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
}
});
this.searchview.on_loaded.add_last(function () {
- self.view_list = new openerp.web.form.SelectCreateListView(self,
+ self.view_list = new instance.web.form.SelectCreateListView(self,
self.dataset, false,
_.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection
}, self.options.list_view_options || {}));
self.view_list.popup = self;
- self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
+ self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() {
self.view_list.do_show();
}).pipe(function() {
self.searchview.do_search();
@@ -2960,7 +3263,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
});
});
});
- this.searchview.appendTo($("#" + this.element_id + "_search"));
+ this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
@@ -2974,14 +3277,14 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
},
create_row: function() {
var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
+ var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain);
wdataset.parent_view = this.options.parent_view;
wdataset.child_name = this.options.child_name;
return wdataset.create.apply(wdataset, arguments);
},
write_row: function() {
var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
+ var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain);
wdataset.parent_view = this.options.parent_view;
wdataset.child_name = this.options.child_name;
return wdataset.write.apply(wdataset, arguments);
@@ -3005,11 +3308,11 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.view_list.$element.hide();
}
this.dataset.index = null;
- this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
+ this.view_form = new instance.web.FormView(this, this.dataset, false, self.options.form_view_options);
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
+ this.view_form.appendTo(this.$element.find(".oe-select-create-popup-view-form"));
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
$buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
@@ -3045,7 +3348,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
on_default_get: function(res) {}
});
-openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
+instance.web.form.SelectCreateListView = instance.web.ListView.extend({
do_add_record: function () {
this.popup.new_object();
},
@@ -3061,9 +3364,9 @@ openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
/**
* @class
- * @extends openerp.web.OldWidget
+ * @extends instance.web.OldWidget
*/
-openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.FormOpenPopup# */{
+instance.web.form.FormOpenPopup = instance.web.OldWidget.extend(/** @lends instance.web.form.FormOpenPopup# */{
template: "FormOpenPopup",
/**
* options:
@@ -3081,7 +3384,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
this.context = context || {};
this.options = _.defaults(options || {}, {"auto_write": true});
this.renderElement();
- openerp.web.dialog(this.$element, {
+ instance.web.dialog(this.$element, {
title: options.title || '',
modal: true,
width: 960,
@@ -3091,7 +3394,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
},
start: function() {
this._super();
- this.dataset = new openerp.web.form.FormOpenDataset(this, this.model, this.context);
+ this.dataset = new instance.web.form.FormOpenDataset(this, this.model, this.context);
this.dataset.fop = this;
this.dataset.ids = [this.row_id];
this.dataset.index = 0;
@@ -3103,7 +3406,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
if (!this.options.auto_write)
return;
var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
+ var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain);
wdataset.parent_view = this.options.parent_view;
wdataset.child_name = this.options.child_name;
wdataset.write(id, data, {}, function(r) {
@@ -3113,14 +3416,14 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
on_write_completed: function() {},
setup_form_view: function() {
var self = this;
- var FormClass = this.options.readonly
- ? openerp.web.views.get_object('page')
- : openerp.web.views.get_object('form');
- this.view_form = new FormClass(this, this.dataset, false, self.options.form_view_options);
+ var FormClass = instance.web.views.get_object('form');
+ var options = _.clone(self.options.form_view_options) || {};
+ options.initial_mode = this.options.readonly ? "view" : "edit";
+ this.view_form = new FormClass(this, this.dataset, false, options);
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
+ this.view_form.appendTo(this.$element.find(".oe-form-open-popup-form-view"));
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
$buttons.html(QWeb.render("FormOpenPopup.form.buttons"));
@@ -3144,7 +3447,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
}
});
-openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
+instance.web.form.FormOpenDataset = instance.web.ProxyDataSet.extend({
read_ids: function() {
if (this.fop.options.read_function) {
return this.fop.options.read_function.apply(null, arguments);
@@ -3154,40 +3457,28 @@ openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
}
});
-openerp.web.form.FieldReference = openerp.web.form.Field.extend({
- form_template: 'FieldReference',
- init: function(view, node) {
- this._super(view, node);
+instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldReference',
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.fields_view = {
fields: {
selection: {
- selection: view.fields_view.fields[this.name].selection
+ selection: this.view.fields_view.fields[this.name].selection
},
m2o: {
relation: null
}
}
};
- this.get_fields_values = view.get_fields_values;
- this.get_selected_ids = view.get_selected_ids;
+ this.get_fields_values = this.view.get_fields_values;
+ this.get_selected_ids = this.view.get_selected_ids;
this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
this.dataset = this.view.dataset;
- this.widgets_counter = 0;
this.view_id = 'reference_' + _.uniqueId();
- this.widgets = {};
this.fields = {};
this.fields_order = [];
- this.selection = new openerp.web.form.FieldSelection(this, { attrs: {
- name: 'selection',
- widget: 'selection'
- }});
this.reference_ready = true;
- this.selection.on_value_changed.add_last(this.on_selection_changed);
- this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o',
- widget: 'many2one'
- }});
- this.m2o.on_ui_change.add_last(this.on_ui_change);
},
on_nop: function() {
},
@@ -3195,59 +3486,82 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
if (this.reference_ready) {
var sel = this.selection.get_value();
this.m2o.field.relation = sel;
- this.m2o.set_value(null);
+ this.m2o.set_value(false);
this.m2o.$element.toggle(sel !== false);
}
},
- start: function() {
- this._super();
+ destroy_content: function() {
+ if (this.selection) {
+ this.selection.destroy();
+ this.selection = undefined;
+ }
+ if (this.m2o) {
+ this.m2o.destroy();
+ this.m2o = undefined;
+ }
+ },
+ initialize_content: function() {
+ this.selection = new instance.web.form.FieldSelection(this, { attrs: {
+ name: 'selection',
+ widget: 'selection'
+ }});
+ this.selection.set({readonly: this.get('effective_readonly')});
+ this.selection.on("change:value", this, this.on_selection_changed);
this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
this.selection.renderElement();
this.selection.start();
+
+ this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
+ name: 'm2o',
+ widget: 'many2one'
+ }});
+ this.m2o.set({"readonly": this.get("effective_readonly")});
+ this.m2o.on("change:value", this, this.data_changed);
this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
this.m2o.renderElement();
this.m2o.start();
},
- is_valid: function() {
- return this.required === false || typeof(this.get_value()) === 'string';
+ is_false: function() {
+ return typeof(this.get_value()) !== 'string';
},
- is_dirty: function() {
- return this.selection.is_dirty() || this.m2o.is_dirty();
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
},
- set_value: function(value) {
- this._super(value);
+ render_value: function() {
this.reference_ready = false;
var vals = [], sel_val, m2o_val;
- if (typeof(value) === 'string') {
- vals = value.split(',');
+ if (typeof(this.get('value')) === 'string') {
+ vals = this.get('value').split(',');
}
sel_val = vals[0] || false;
m2o_val = vals[1] ? parseInt(vals[1], 10) : false;
- this.selection.set_value(sel_val);
+ if (!this.get("effective_readonly")) {
+ this.selection.set_value(sel_val);
+ }
this.m2o.field.relation = sel_val;
this.m2o.set_value(m2o_val);
this.m2o.$element.toggle(sel_val !== false);
this.reference_ready = true;
},
- get_value: function() {
+ data_changed: function() {
var model = this.selection.get_value(),
id = this.m2o.get_value();
if (typeof(model) === 'string' && typeof(id) === 'number') {
- return model + ',' + id;
+ this.set({'value': model + ',' + id});
} else {
- return false;
+ this.set({'value': false});
}
- }
-});
+ },
+}));
-openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
+instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.iframe = this.element_id + '_iframe';
this.binary_value = false;
},
- start: function() {
- this._super.apply(this, arguments);
+ initialize_content: function() {
this.$element.find('input.oe-binary-file').change(this.on_file_change);
this.$element.find('button.oe-binary-file-save').click(this.on_save_as);
this.$element.find('.oe-binary-file-clear').click(this.on_clear);
@@ -3281,7 +3595,6 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
console.warn("Error while uploading file : ", name);
} else {
this.on_file_uploaded_and_valid.apply(this, arguments);
- this.on_ui_change();
}
this.$element.find('.oe-binary-progress').hide();
this.$element.find('.oe-binary').show();
@@ -3300,48 +3613,64 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
context: this.view.dataset.get_context()
})},
complete: $.unblockUI,
- error: openerp.webclient.crashmanager.on_rpc_error
+ error: instance.webclient.crashmanager.on_rpc_error
});
},
on_clear: function() {
- if (this.value !== false) {
- this.value = false;
+ if (this.get('value') !== false) {
this.binary_value = false;
- this.on_ui_change();
+ this.set({'value': false});
}
return false;
}
-});
+}));
-openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
- form_template: 'FieldBinaryFile',
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
- this.$element.find('input[type=text]').prop('readonly', this.readonly);
+instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
+ template: 'FieldBinaryFile',
+ initialize_content: function() {
+ this._super();
+ if (this.get("effective_readonly")) {
+ var self = this;
+ this.$element.find('a').click(function() {
+ if (self.get('value')) {
+ self.on_save_as();
+ }
+ return false;
+ });
+ }
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- var show_value;
- if (this.node.attrs.filename) {
- show_value = this.view.datarecord[this.node.attrs.filename] || '';
+ 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 {
+ show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : '';
+ }
+ this.$element.find('input').eq(0).val(show_value);
} else {
- show_value = (value != null && value !== false) ? value : '';
+ this.$element.find('a').show(!!this.get('value'));
+ if (this.get('value')) {
+ var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
+ this.$element.find('a').text(show_value);
+ }
}
- this.$element.find('input').eq(0).val(show_value);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.value = file_base64;
this.binary_value = true;
+ this.set({'value': file_base64});
var show_value = name + " (" + this.human_filesize(size) + ")";
this.$element.find('input').eq(0).val(show_value);
this.set_filename(name);
},
- set_filename: function(value) {
+ 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();
+ this.view.fields[filename].set({value: value_});
}
},
on_clear: function() {
@@ -3351,58 +3680,59 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
}
});
-openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
- form_template: 'FieldBinaryImage',
- start: function() {
- this._super.apply(this, arguments);
- this.$image = this.$element.find('img.oe-binary-image');
+instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
+ template: 'FieldBinaryImage',
+ initialize_content: function() {
+ this._super();
+ this.$placeholder = $(".oe_form_field-binary-image-placeholder", this.$element);
+ if (!this.get("effective_readonly"))
+ this.$element.find('.oe-binary').show();
+ else
+ this.$element.find('.oe-binary').hide();
},
- update_dom: function() {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- this.$element.find('.oe-binary').toggle(!this.readonly);
+ this.render_value();
},
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.set_image_maxwidth();
-
+ render_value: function() {
var url;
- if (value && value.substr(0, 10).indexOf(' ') == -1) {
- url = 'data:image/png;base64,' + this.value;
- } else {
+ if (this.get('value') && this.get('value').substr(0, 10).indexOf(' ') == -1) {
+ url = 'data:image/png;base64,' + this.get('value');
+ } else if (this.get('value')) {
url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ } else {
+ url = "/web/static/src/img/placeholder.png";
}
- this.$image.attr('src', url);
- },
- set_image_maxwidth: function() {
- this.$image.css('max-width', this.$element.width());
+ var rendered = QWeb.render("FieldBinaryImage-img", {widget: this, url: url});;
+ this.$placeholder.html(rendered);
},
on_file_change: function() {
- this.set_image_maxwidth();
+ this.render_value();
this._super.apply(this, arguments);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.value = file_base64;
+ this.set({'value': file_base64});
this.binary_value = true;
- this.$image.attr('src', 'data:' + (content_type || 'image/png') + ';base64,' + file_base64);
+ this.render_value();
},
on_clear: function() {
this._super.apply(this, arguments);
- this.$image.attr('src', '/web/static/src/img/placeholder.png');
+ this.render_value();
}
});
-openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
- form_template: "EmptyComponent",
+instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
+ template: "EmptyComponent",
start: function() {
this._super();
this.selected_value = null;
this.render_list();
},
- set_value: function(value) {
- this._super(value);
- this.selected_value = value;
+ set_value: function(value_) {
+ this._super(value_);
+ this.selected_value = value_;
this.render_list();
},
@@ -3420,7 +3750,7 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
});
}
- var content = openerp.web.qweb.render("FieldStatus.content", {widget: this, _:_});
+ var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_});
this.$element.html(content);
var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
@@ -3457,77 +3787,36 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
}
});
-openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
- renderElement: function () {
- var $root = $('<div class="oe_form_html_view">');
- this.render_children(this, $root);
- var rendered = $root.html();
- this.$element.html(rendered);
- },
- render_children: function (object, $into) {
- var self = this,
- fields = this.view.fields_view.fields;
- _(object.children).each(function (child) {
- if (typeof child === 'string') {
- $into.text(child);
- } else if (child.tag === 'field') {
- var widget = instanciate_widget(self.view.registry.get_object('frame'),
- self.view, {tag: 'ueule', attrs: {}, children: [child] });
- widget.appendTo($into);
- } else {
- var $child = $(document.createElement(child.tag))
- .attr(child.attrs)
- .appendTo($into);
- self.render_children(child, $child);
- }
- });
- }
-});
-
-
/**
- * Registry of form widgets, called by :js:`openerp.web.FormView`
+ * Registry of form widgets, called by :js:`instance.web.FormView`
*/
-openerp.web.form.widgets = new openerp.web.Registry({
- 'frame' : 'openerp.web.form.WidgetFrame',
- 'group' : 'openerp.web.form.WidgetGroup',
- 'notebook' : 'openerp.web.form.WidgetNotebook',
- 'notebookpage' : 'openerp.web.form.WidgetNotebookPage',
- 'separator' : 'openerp.web.form.WidgetSeparator',
- 'label' : 'openerp.web.form.WidgetLabel',
- 'button' : 'openerp.web.form.WidgetButton',
- 'char' : 'openerp.web.form.FieldChar',
- 'id' : 'openerp.web.form.FieldID',
- 'email' : 'openerp.web.form.FieldEmail',
- 'url' : 'openerp.web.form.FieldUrl',
- 'text' : 'openerp.web.form.FieldText',
- 'date' : 'openerp.web.form.FieldDate',
- 'datetime' : 'openerp.web.form.FieldDatetime',
- 'selection' : 'openerp.web.form.FieldSelection',
- 'many2one' : 'openerp.web.form.FieldMany2One',
- 'many2many' : 'openerp.web.form.FieldMany2Many',
- 'one2many' : 'openerp.web.form.FieldOne2Many',
- 'one2many_list' : 'openerp.web.form.FieldOne2Many',
- 'reference' : 'openerp.web.form.FieldReference',
- 'boolean' : 'openerp.web.form.FieldBoolean',
- 'float' : 'openerp.web.form.FieldFloat',
- 'integer': 'openerp.web.form.FieldFloat',
- 'float_time': 'openerp.web.form.FieldFloat',
- 'progressbar': 'openerp.web.form.FieldProgressBar',
- 'image': 'openerp.web.form.FieldBinaryImage',
- 'binary': 'openerp.web.form.FieldBinaryFile',
- 'statusbar': 'openerp.web.form.FieldStatus',
- 'html': 'openerp.web.form.WidgetHtml'
+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',
+ 'date' : 'instance.web.form.FieldDate',
+ 'datetime' : 'instance.web.form.FieldDatetime',
+ 'selection' : 'instance.web.form.FieldSelection',
+ 'many2one' : 'instance.web.form.FieldMany2One',
+ 'many2many' : 'instance.web.form.FieldMany2Many',
+ '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'
});
-var instanciate_widget = function(claz, view, node, o1, o2) {
- var widget = new (claz)(view, node, o1, o2);
- widget.element_class = (['formview', view.view_id, widget.element_name,
- view.widgets_counter++].join("_")).replace(/[^\r\n\f0-9A-Za-z_-]/g, "_");
- view.widgets[widget.element_class] = widget;
- return widget;
-}
-
+instance.web.form.tags = new instance.web.Registry({
+ 'button' : 'instance.web.form.WidgetButton',
+});
};
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index e8fff450c6e..c29957cac2d 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -1,9 +1,9 @@
-openerp.web.list = function (openerp) {
-var _t = openerp.web._t,
- _lt = openerp.web._lt;
-var QWeb = openerp.web.qweb;
-openerp.web.views.add('list', 'openerp.web.ListView');
-openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# */ {
+openerp.web.list = function (instance) {
+var _t = instance.web._t,
+ _lt = instance.web._lt;
+var QWeb = instance.web.qweb;
+instance.web.views.add('list', 'instance.web.ListView');
+instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListView# */ {
_template: 'ListView',
display_name: _lt('List'),
defaults: {
@@ -33,11 +33,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* See constructor parameters and method documentations for information on
* the default behaviors and possible options for the list view.
*
- * @constructs openerp.web.ListView
- * @extends openerp.web.View
+ * @constructs instance.web.ListView
+ * @extends instance.web.View
*
* @param parent parent object
- * @param {openerp.web.DataSet} dataset the dataset the view should work with
+ * @param {instance.web.DataSet} dataset the dataset the view should work with
* @param {String} view_id the listview's identifier, if any
* @param {Object} options A set of options used to configure the view
* @param {Boolean} [options.selectable=true] determines whether view rows are selectable (e.g. via a checkbox)
@@ -61,12 +61,12 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.records = new Collection();
- this.set_groups(new openerp.web.ListView.Groups(this));
+ this.set_groups(new instance.web.ListView.Groups(this));
- if (this.dataset instanceof openerp.web.DataSetStatic) {
- this.groups.datagroup = new openerp.web.StaticDataGroup(this.dataset);
+ if (this.dataset instanceof instance.web.DataSetStatic) {
+ this.groups.datagroup = new instance.web.StaticDataGroup(this.dataset);
} else {
- this.groups.datagroup = new openerp.web.DataGroup(
+ this.groups.datagroup = new instance.web.DataGroup(
this, this.model,
dataset.get_domain(),
dataset.get_context(),
@@ -104,7 +104,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
/**
* Set a custom Group construct as the root of the List View.
*
- * @param {openerp.web.ListView.Groups} groups
+ * @param {instance.web.ListView.Groups} groups
*/
set_groups: function (groups) {
var self = this;
@@ -136,7 +136,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @returns {$.Deferred} loading promise
*/
start: function() {
- this._super();
this.$element.addClass('oe-listview');
return this.reload_view(null, null, true);
},
@@ -208,6 +207,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.$element.html(QWeb.render(this._template, this));
// Head hook
+ // Selecting records
this.$element.find('.all-record-selector').click(function(){
self.$element.find('.oe-record-selector input').prop('checked',
self.$element.find('.all-record-selector').prop('checked') || false);
@@ -216,70 +216,95 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
'selected', [selection.ids, selection.records]);
});
- this.$element.find('.oe-list-add')
- .click(this.proxy('do_add_record'))
- .attr('disabled', grouped && this.options.editable);
- this.$element.find('.oe-list-delete')
- .attr('disabled', true)
- .click(this.proxy('do_delete_selected'));
+ // Sorting columns
this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) {
e.stopPropagation();
-
var $this = $(this);
self.dataset.sort($this.data('id'));
if ($this.find('span').length) {
- $this.find('span').toggleClass(
- 'ui-icon-triangle-1-s ui-icon-triangle-1-n');
+ $this.find('span').toggleClass( 'ui-icon-triangle-1-s ui-icon-triangle-1-n');
} else {
- $this.append('<span class="ui-icon ui-icon-triangle-1-n">')
- .siblings('.oe-sortable').find('span').remove();
+ $this.append('<span class="ui-icon ui-icon-triangle-1-n">') .siblings('.oe-sortable').find('span').remove();
}
self.reload_content();
});
- this.$element.find('.oe-list-pager')
- .delegate('button', 'click', function () {
- var $this = $(this);
- switch ($this.data('pager-action')) {
- case 'first':
- self.page = 0; break;
- case 'last':
- self.page = Math.floor(
- self.dataset.size() / self.limit());
- break;
- case 'next':
- self.page += 1; break;
- case 'previous':
- self.page -= 1; break;
- }
- self.reload_content();
- }).find('.oe-pager-state')
- .click(function (e) {
- e.stopPropagation();
+ // Add and delete
+ if (!this.$buttons) {
+ this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
+ if (this.options.$buttons) {
+ this.$buttons.appendTo(this.options.$buttons);
+ } else {
+ this.$element.find('.oe_list_buttons').replaceWith(this.$buttons);
+ }
+ this.$buttons.find('.oe_list_add')
+ .click(this.proxy('do_add_record'))
+ .prop('disabled', grouped && this.options.editable)
+ .end()
+ .find('.oe_list_delete')
+ .click(this.proxy('do_delete_selected'))
+ .prop('disabled', true);
+ }
+
+ // Pager
+ if (!this.$pager) {
+ this.$pager = $(QWeb.render("ListView.pager", {'widget':self}));
+ if (this.options.$buttons) {
+ this.$pager.appendTo(this.options.$pager);
+ } else {
+ this.$element.find('.oe_list_pager').replaceWith(this.$pager);
+ }
+
+ this.$pager
+ .on('click', 'a[data-pager-action]', function () {
var $this = $(this);
+ var max_page = Math.floor(self.dataset.size() / self.limit());
+ switch ($this.data('pager-action')) {
+ case 'first':
+ self.page = 0; break;
+ case 'last':
+ self.page = max_page - 1;
+ break;
+ case 'next':
+ self.page += 1; break;
+ case 'previous':
+ self.page -= 1; break;
+ }
+ if (self.page < 0) {
+ self.page = max_page;
+ } else if (self.page > max_page) {
+ self.page = 0;
+ }
+ self.reload_content();
+ }).find('.oe-pager-state')
+ .click(function (e) {
+ e.stopPropagation();
+ var $this = $(this);
+
+ var $select = $('<select>')
+ .appendTo($this.empty())
+ .click(function (e) {e.stopPropagation();})
+ .append('<option value="80">80</option>' +
+ '<option value="100">100</option>' +
+ '<option value="200">200</option>' +
+ '<option value="500">500</option>' +
+ '<option value="NaN">' + _t("Unlimited") + '</option>')
+ .change(function () {
+ var val = parseInt($select.val(), 10);
+ self._limit = (isNaN(val) ? null : val);
+ self.page = 0;
+ self.reload_content();
+ })
+ .val(self._limit || 'NaN');
+ });
+ }
- var $select = $('<select>')
- .appendTo($this.empty())
- .click(function (e) {e.stopPropagation();})
- .append('<option value="80">80</option>' +
- '<option value="100">100</option>' +
- '<option value="200">200</option>' +
- '<option value="500">500</option>' +
- '<option value="NaN">' + _t("Unlimited") + '</option>')
- .change(function () {
- var val = parseInt($select.val(), 10);
- self._limit = (isNaN(val) ? null : val);
- self.page = 0;
- self.reload_content();
- })
- .val(self._limit || 'NaN');
- });
- if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
- this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
- this.sidebar.start();
+ // Sidebar
+ if (!this.sidebar && this.options.$sidebar) {
+ this.sidebar = new instance.web.Sidebar(this);
+ this.sidebar.appendTo(this.options.$sidebar);
this.sidebar.add_toolbar(this.fields_view.toolbar);
- this.set_common_sidebar_sections(this.sidebar);
}
},
/**
@@ -287,7 +312,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*
* Horrifying side-effect: sets the dataset's data on this.dataset?
*
- * @param {openerp.web.DataSet} dataset
+ * @param {instance.web.DataSet} dataset
*/
configure_pager: function (dataset) {
this.dataset.ids = dataset.ids;
@@ -296,28 +321,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.dataset._length = dataset._length;
}
- var limit = this.limit(),
- total = dataset.size(),
- first = (this.page * limit),
- last;
- if (!limit || (total - first) < limit) {
- last = total;
- } else {
- last = first + limit;
- }
- this.$element.find('span.oe-pager-state').empty().text(_.str.sprintf(
- _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), {
- first_record: first + 1,
- last_record: last,
- records_count: total
- }));
+ var page = this.page + 1,
+ total = Math.floor(dataset.size() / this.limit()) + 1;
- this.$element
- .find('button[data-pager-action=first], button[data-pager-action=previous]')
- .attr('disabled', this.page === 0)
- .end()
- .find('button[data-pager-action=last], button[data-pager-action=next]')
- .attr('disabled', last === total);
+ this.$pager.find('.oe-pager-state').text(isNaN(total)
+ ? '-' : _.str.sprintf('%d / %d', page, total));
},
/**
* Sets up the listview's columns: merges view and fields data, move
@@ -328,7 +336,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {Boolean} [grouped] Should the grouping columns (group and count) be displayed
*/
setup_columns: function (fields, grouped) {
- var domain_computer = openerp.web.form.compute_domain;
+ var domain_computer = instance.web.form.compute_domain;
var noop = function () { return {}; };
var field_to_column = function (field) {
@@ -397,7 +405,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*
* The default implementation asks the list view's view manager to switch
* to a different view (by calling
- * :js:func:`~openerp.web.ViewManager.on_mode_switch`), using the
+ * :js:func:`~instance.web.ViewManager.on_mode_switch`), using the
* provided record index (within the current list view's dataset).
*
* If the index is null, ``switch_to_record`` asks for the creation of a
@@ -407,7 +415,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {String} [view="page"] the view type to switch to
*/
select_record:function (index, view) {
- view = view || index == null ? 'form' : 'page';
+ view = view || index == null ? 'form' : 'form';
this.dataset.index = index;
_.delay(_.bind(function () {
this.do_switch_view(view);
@@ -418,12 +426,24 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
if (this.sidebar) {
this.sidebar.$element.show();
}
+ if (this.$buttons) {
+ this.$buttons.show();
+ }
+ if (this.$pager) {
+ this.$pager.show();
+ }
},
do_hide: function () {
- this._super();
if (this.sidebar) {
this.sidebar.$element.hide();
}
+ if (this.$buttons) {
+ this.$buttons.hide();
+ }
+ if (this.$pager) {
+ this.$pager.hide();
+ }
+ this._super();
},
/**
* Reloads the list view based on the current settings (dataset & al)
@@ -444,7 +464,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
view_id: this.view_id,
view_type: "tree",
context: this.dataset.get_context(context),
- toolbar: this.options.sidebar
+ toolbar: !!this.options.$sidebar
}, callback);
}
},
@@ -479,7 +499,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
reload: function () {
return this.reload_content();
},
-
do_load_state: function(state, warm) {
var reload = false;
if (state.page && this.page !== state.page) {
@@ -507,7 +526,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*/
do_search: function (domain, context, group_by) {
this.page = 0;
- this.groups.datagroup = new openerp.web.DataGroup(
+ this.groups.datagroup = new instance.web.DataGroup(
this, this.model, domain, context, group_by);
this.groups.datagroup.sort = this.dataset._sort;
@@ -544,16 +563,20 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {Array} records selected record values
*/
do_select: function (ids, records) {
- this.$element.find('.oe-list-delete').attr('disabled', !ids.length);
+ this.$buttons.find('.oe_list_delete').attr('disabled', !ids.length);
if (!ids.length) {
this.dataset.index = 0;
- if (this.sidebar) { this.sidebar.do_fold(); }
+ if (this.sidebar) {
+ this.sidebar.$element.hide();
+ }
this.compute_aggregates();
return;
}
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
- if (this.sidebar) { this.sidebar.do_unfold(); }
+ if (this.sidebar) {
+ this.sidebar.$element.show();
+ }
this.compute_aggregates(_(records).map(function (record) {
return {count: 1, values: record};
@@ -575,7 +598,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
return;
}
- var c = new openerp.web.CompoundContext();
+ var c = new instance.web.CompoundContext();
c.set_eval_context(_.extend({
active_id: id,
active_ids: [id],
@@ -592,7 +615,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*
* @param {Number} index index of the record in the dataset
* @param {Object} id identifier of the activated record
- * @param {openerp.web.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups)
+ * @param {instance.web.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups)
*/
do_activate_record: function (index, id, dataset, view) {
this.dataset.ids = dataset.ids;
@@ -616,11 +639,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
/**
* Computes the aggregates for the current list view, either on the
* records provided or on the records of the internal
- * :js:class:`~openerp.web.ListView.Group`, by calling
- * :js:func:`~openerp.web.ListView.group.get_records`.
+ * :js:class:`~instance.web.ListView.Group`, by calling
+ * :js:func:`~instance.web.ListView.group.get_records`.
*
* Then displays the aggregates in the table through
- * :js:method:`~openerp.web.ListView.display_aggregates`.
+ * :js:method:`~instance.web.ListView.display_aggregates`.
*
* @param {Array} [records]
*/
@@ -696,7 +719,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
}
$footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id))
- .html(openerp.web.format_cell(aggregation, column, {
+ .html(instance.web.format_cell(aggregation, column, {
process_modifiers: false
}));
});
@@ -747,9 +770,20 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
.attr('colspan', this.previous_colspan);
this.previous_colspan = null;
}
+ },
+ no_result: function () {
+ if (this.groups.group_by
+ || !this.options.action
+ || !this.options.action.help) {
+ return;
+ }
+ this.$element.children('table').replaceWith(
+ $('<div class="oe_listview_nocontent">')
+ .append($('<img>', { src: '/web/static/src/img/list_empty_arrow.png' }))
+ .append($('<div>').html(this.options.action.help)));
}
});
-openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.ListView.List# */{
+instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{
/**
* List display for the ListView, handles basic DOM events and transforms
* them in the relevant higher-level events, to which the list view (or
@@ -774,10 +808,10 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
* Triggered when a row of the table is clicked, provides the index (in
* the rows array) and id of the selected record to the handle function.
*
- * @constructs openerp.web.ListView.List
- * @extends openerp.web.Class
+ * @constructs instance.web.ListView.List
+ * @extends instance.web.Class
*
- * @param {Object} opts display options, identical to those of :js:class:`openerp.web.ListView`
+ * @param {Object} opts display options, identical to those of :js:class:`instance.web.ListView`
*/
init: function (group, opts) {
var self = this;
@@ -824,7 +858,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
this.records.bind(event, callback);
}, this);
- this.$_element = $('<tbody class="ui-widget-content">')
+ this.$_element = $('<tbody>')
.appendTo(document.body)
.delegate('th.oe-record-selector', 'click', function (e) {
e.stopPropagation();
@@ -892,7 +926,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
- new openerp.web.DataSet(this.view, model).name_get([id], function(names) {
+ new instance.web.DataSet(this.view, model).name_get([id], function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
@@ -907,14 +941,14 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
// fetch the name, set it on the record (in the right field)
// and let the various registered events handle refreshing the
// row
- new openerp.web.DataSet(this.view, column.relation)
+ new instance.web.DataSet(this.view, column.relation)
.name_get([value], function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
}
}
- return openerp.web.format_cell(record.toForm().data, column, {
+ return instance.web.format_cell(record.toForm().data, column, {
model: this.dataset.model,
id: record.get('id')
});
@@ -1068,19 +1102,19 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
});
}
});
-openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.ListView.Groups# */{
+instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.web.ListView.Groups# */{
passtrough_events: 'action deleted row_link',
/**
* Grouped display for the ListView. Handles basic DOM events and interacts
- * with the :js:class:`~openerp.web.DataGroup` bound to it.
+ * with the :js:class:`~instance.web.DataGroup` bound to it.
*
* Provides events similar to those of
- * :js:class:`~openerp.web.ListView.List`
+ * :js:class:`~instance.web.ListView.List`
*
- * @constructs openerp.web.ListView.Groups
- * @extends openerp.web.Class
+ * @constructs instance.web.ListView.Groups
+ * @extends instance.web.Class
*
- * @param {openerp.web.ListView} view
+ * @param {instance.web.ListView} view
* @param {Object} [options]
* @param {Collection} [options.records]
* @param {Object} [options.options]
@@ -1191,7 +1225,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
self.records.proxy(group.value).reset();
delete self.children[group.value];
}
- var child = self.children[group.value] = new openerp.web.ListView.Groups(self.view, {
+ var child = self.children[group.value] = new instance.web.ListView.Groups(self.view, {
records: self.records.proxy(group.value),
options: self.options,
columns: self.columns
@@ -1227,7 +1261,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
var group_column = _(self.columns).detect(function (column) {
return column.id === group.grouped_on; });
try {
- $group_column.html(openerp.web.format_cell(
+ $group_column.html(instance.web.format_cell(
row_data, group_column, {
value_if_empty: _t("Undefined"),
process_modifiers: false
@@ -1264,7 +1298,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
var r = {};
r[column.id] = {value: group.aggregates[column.id]};
$('<td class="oe-number">')
- .html(openerp.web.format_cell(
+ .html(instance.web.format_cell(
r, column, {process_modifiers: false}))
.appendTo($row);
} else {
@@ -1297,7 +1331,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
},
render_dataset: function (dataset) {
var self = this,
- list = new openerp.web.ListView.List(this, {
+ list = new instance.web.ListView.List(this, {
options: this.options,
columns: this.columns,
dataset: dataset,
@@ -1344,6 +1378,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
self.records.add(records, {silent: true});
list.render();
d.resolve(list);
+ if (_.isEmpty(records)) {
+ view.no_result();
+ }
});});
return d.promise();
},
@@ -1510,10 +1547,10 @@ var Events = /** @lends Events# */{
return this;
}
};
-var Record = openerp.web.Class.extend(/** @lends Record# */{
+var Record = instance.web.Class.extend(/** @lends Record# */{
/**
* @constructs Record
- * @extends openerp.web.Class
+ * @extends instance.web.Class
*
* @mixes Events
* @param {Object} [data]
@@ -1591,7 +1628,7 @@ var Record = openerp.web.Class.extend(/** @lends Record# */{
}
});
Record.include(Events);
-var Collection = openerp.web.Class.extend(/** @lends Collection# */{
+var Collection = instance.web.Class.extend(/** @lends Collection# */{
/**
* Smarter collections, with events, very strongly inspired by Backbone's.
*
@@ -1599,7 +1636,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{
* various serious
*
* @constructs Collection
- * @extends openerp.web.Class
+ * @extends instance.web.Class
*
* @mixes Events
* @param {Array} [records] records to initialize the collection with
@@ -1631,19 +1668,19 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{
var records = record instanceof Array ? record : [record];
for(var i=0, length=records.length; i<length; ++i) {
- var instance = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
- instance.bind(null, this._onRecordEvent);
- this._byId[instance.get('id')] = instance;
+ var instance_ = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
+ instance_.bind(null, this._onRecordEvent);
+ this._byId[instance_.get('id')] = instance_;
if (options.at == undefined) {
- this.records.push(instance);
+ this.records.push(instance_);
if (!options.silent) {
- this.trigger('add', this, instance, this.records.length-1);
+ this.trigger('add', this, instance_, this.records.length-1);
}
} else {
var insertion_index = options.at + i;
- this.records.splice(insertion_index, 0, instance);
+ this.records.splice(insertion_index, 0, instance_);
if (!options.silent) {
- this.trigger('add', this, instance, insertion_index);
+ this.trigger('add', this, instance_, insertion_index);
}
}
this.length++;
@@ -1774,7 +1811,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{
}
});
Collection.include(Events);
-openerp.web.list = {
+instance.web.list = {
Events: Events,
Record: Record,
Collection: Collection
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index b8699bcc006..ac19e3f341d 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -2,16 +2,16 @@
* handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
* @namespace
*/
-openerp.web.list_editable = function (openerp) {
+openerp.web.list_editable = function (instance) {
var KEY_RETURN = 13,
KEY_ESCAPE = 27;
- var QWeb = openerp.web.qweb;
+ var QWeb = instance.web.qweb;
// editability status of list rows
- openerp.web.ListView.prototype.defaults.editable = null;
+ instance.web.ListView.prototype.defaults.editable = null;
// TODO: not sure second @lends on existing item is correct, to check
- openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
+ instance.web.ListView.include(/** @lends instance.web.ListView# */{
init: function () {
var self = this;
this._super.apply(this, arguments);
@@ -37,7 +37,7 @@ openerp.web.list_editable = function (openerp) {
*
* @param {Number} index index of the record in the dataset
* @param {Object} id identifier of the record being edited
- * @param {openerp.web.DataSet} dataset dataset in which the record is available
+ * @param {instance.web.DataSet} dataset dataset in which the record is available
*/
do_edit: function (index, id, dataset) {
_.extend(this.dataset, dataset);
@@ -93,8 +93,8 @@ openerp.web.list_editable = function (openerp) {
}
});
- openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
- passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
+ instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{
+ passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved",
new_record: function () {
// TODO: handle multiple children
this.children[null].new_record();
@@ -113,7 +113,7 @@ openerp.web.list_editable = function (openerp) {
}
});
- openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{
+ instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
row_clicked: function (event) {
if (!this.options.editable) {
return this._super.apply(this, arguments);
@@ -235,7 +235,8 @@ openerp.web.list_editable = function (openerp) {
}
self.edition = true;
self.edition_id = record_id;
- self.edition_form = new openerp.web.ListEditableFormView(self.view, self.dataset, false);
+ $new_row.addClass("oe_form_container");
+ self.edition_form = new instance.web.ListEditableFormView(self.view, self.dataset, false);
self.edition_form.$element = $new_row;
self.edition_form.editable_list = self;
// HO HO
@@ -273,7 +274,7 @@ openerp.web.list_editable = function (openerp) {
// insert after the source record
var index = self.records.indexOf(
self.records.get(source_record_id)) + 1;
- record = new openerp.web.list.Record({id: id});
+ record = new instance.web.list.Record({id: id});
self.records.add(record, {at: index});
self.dataset.ids.splice(index, 0, id);
}
@@ -366,52 +367,57 @@ openerp.web.list_editable = function (openerp) {
});
}
});
- if (!openerp.web.list) {
- openerp.web.list = {};
- }
- if (!openerp.web.list.form) {
- openerp.web.list.form = {};
- }
- openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
- form_template: 'ListView.row.frame'
- });
- var form_widgets = openerp.web.form.widgets;
- openerp.web.list.form.widgets = form_widgets.extend({
- 'frame': 'openerp.web.list.form.WidgetFrame'
- });
- // All form widgets inherit a problematic behavior from
- // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
- // whether it's @invisible or @attrs[invisible]. In list view, only the
- // former should completely remove the cell. We need to override update_dom
- // on all widgets since we can't just hit on widget itself (I think)
- var list_form_widgets = openerp.web.list.form.widgets;
- _(form_widgets.map).each(function (widget_path, key) {
- if (key === 'frame') { return; }
- var new_path = 'openerp.web.list.form.' + key;
-
- openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
- update_dom: function () {
- this.$element.children().css('visibility', '');
- if (this.modifiers.tree_invisible) {
- var old_invisible = this.invisible;
- this.invisible = true;
- this._super();
- this.invisible = old_invisible;
- } else if (this.invisible) {
- this.$element.children().css('visibility', 'hidden');
- } else {
- this._super();
- }
- }
- });
- list_form_widgets.add(key, new_path);
- });
- openerp.web.ListEditableFormView = openerp.web.FormView.extend({
- form_template: 'ListView.row.form',
+ instance.web.ListEditableFormView = instance.web.FormView.extend({
init: function() {
- this._super.apply(this, arguments);
- this.registry = openerp.web.list.form.widgets;
+ this._super.apply(this, arguments);
+ this.rendering_engine = new instance.web.ListEditableRenderingEngine(this);
+ },
+ renderElement: function() {}
+ });
+
+ instance.web.ListEditableRenderingEngine = instance.web.Class.extend({
+ init: function(view) {
+ this.view = view;
+ },
+ set_fields_view: function(fields_view) {
+ this.fvg = fields_view;
+ },
+ set_tags_registry: function(tags_registry) {
+ this.tags_registry = tags_registry;
+ },
+ set_fields_registry: function(fields_registry) {
+ this.fields_registry = fields_registry;
+ },
+ render_to: function($element) {
+ var self = this;
+
+ var xml = instance.web.json_node_to_xml(this.fvg.arch);
+ var $xml = $(xml);
+
+ if (this.view.editable_list.options.selectable)
+ $("<td>").appendTo($element);
+ if (this.view.editable_list.options.isClarkGable)
+ $("<td>").appendTo($element);
+
+ $xml.children().each(function(i, el) {
+ var modifiers = JSON.parse($(el).attr("modifiers") || "{}");
+ var $td = $("<td>");
+ if (modifiers.tree_invisible === true)
+ $td.hide();
+ var tag_name = el.tagName.toLowerCase();
+ var key = tag_name;
+ if (tag_name === "field") {
+ var name = $(el).attr("name");
+ key = $(el).attr('widget') || self.fvg.fields[name].type;
+ }
+ var obj = self.view.fields_registry.get_object(key);
+ var w = new (obj)(self.view, instance.web.xml_to_json(el));
+ self.view.register_field(w, $(el).attr("name"));
+ w.appendTo($td);
+ $td.appendTo($element);
+ });
+ $("<td><button class='oe-edit-row-save' type='button'></button></td>").appendTo($element);
},
});
};
diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js
deleted file mode 100644
index 8949169838a..00000000000
--- a/addons/web/static/src/js/view_page.js
+++ /dev/null
@@ -1,288 +0,0 @@
-openerp.web.page = function (openerp) {
- var _t = openerp.web._t,
- _lt = openerp.web._lt;
-
- openerp.web.views.add('page', 'openerp.web.PageView');
- openerp.web.PageView = openerp.web.FormView.extend({
- form_template: "PageView",
- display_name: _lt('Page'),
- init: function () {
- this._super.apply(this, arguments);
- this.registry = openerp.web.page.readonly;
- },
- reload: function () {
- if (this.dataset.index == null) {
- this.do_prev_view();
- return $.Deferred().reject().promise();
- }
- return this._super();
- },
- on_loaded: function(data) {
- this._super(data);
- this.$form_header.find('button.oe_form_button_edit').click(this.on_button_edit);
- this.$form_header.find('button.oe_form_button_create').click(this.on_button_create);
- this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
- this.$form_header.find('button.oe_form_button_delete').click(this.on_button_delete);
- },
- on_button_edit: function() {
- return this.do_switch_view('form');
- },
- on_button_create: function() {
- this.dataset.index = null;
- return this.do_switch_view('form');
- },
- 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() {
- return self.do_switch_view('form');
- }).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();
- }
- });
-
- /** @namespace */
- openerp.web.page = {};
-
- openerp.web.page.WidgetFrameReadonly = openerp.web.form.WidgetFrame.extend({
- form_template: 'WidgetFrame.readonly'
- });
- openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({
-
- });
- openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({
- form_template: 'FieldChar.readonly',
- init: function(view, node) {
- this._super(view, node);
- this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
- },
- set_value: function (value) {
- this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- if (this.password) {
- show_value = new Array(show_value.length + 1).join('*');
- }
- this.$element.find('div').text(show_value);
- return show_value;
- }
- });
- openerp.web.page.FieldFloatReadonly = openerp.web.page.FieldCharReadonly.extend({
- init: function (view, node) {
- this._super(view, node);
- if (node.attrs.digits) {
- this.digits = py.eval(node.attrs.digits);
- } else {
- this.digits = view.fields_view.fields[node.attrs.name].digits;
- }
- }
- });
- openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({
- form_template: 'FieldURI.readonly',
- scheme: null,
- format_value: function (value) {
- return value;
- },
- set_value: function (value) {
- if (!value) {
- this.$element.find('a').text('').attr('href', '#');
- return;
- }
- this.$element.find('a')
- .attr('href', this.scheme + ':' + value)
- .text(this.format_value(value));
- }
- });
- openerp.web.page.FieldEmailReadonly = openerp.web.page.FieldURIReadonly.extend({
- scheme: 'mailto'
- });
- openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({
- set_value: function (value) {
- if (!value) {
- this.$element.find('a').text('').attr('href', '#');
- return;
- }
- var s = /(\w+):(.+)/.exec(value);
- if (!s) {
- value = "http://" + value;
- }
- this.$element.find('a').attr('href', value).text(value);
- }
- });
- openerp.web.page.FieldBooleanReadonly = openerp.web.form.FieldBoolean.extend({
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', true);
- }
- });
- openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({
- form_template: 'FieldChar.readonly',
- init: function(view, node) {
- // lifted straight from r/w version
- var self = this;
- this._super(view, node);
- this.values = _.clone(this.field.selection);
- _.each(this.values, function(v, i) {
- if (v[0] === false && v[1] === '') {
- self.values.splice(i, 1);
- }
- });
- this.values.unshift([false, '']);
- },
- set_value: function (value) {
- value = value === null ? false : value;
- value = value instanceof Array ? value[0] : value;
- var option = _(this.values)
- .detect(function (record) { return record[0] === value; });
- this._super(value);
- this.$element.find('div').text(option ? option[1] : this.values[0][1]);
- }
- });
- openerp.web.page.FieldMany2OneReadonly = openerp.web.page.FieldURIReadonly.extend({
- set_value: function (value) {
- value = value || null;
- this.invalid = false;
- var self = this;
- this.value = value;
- self.update_dom();
- self.on_value_changed();
- var real_set_value = function(rval) {
- self.value = rval;
- self.$element.find('a')
- .unbind('click')
- .text(rval ? rval[1] : '')
- .click(function () {
- self.do_action({
- type: 'ir.actions.act_window',
- res_model: self.field.relation,
- res_id: self.value[0],
- context: self.build_context(),
- views: [[false, 'page'], [false, 'form']],
- target: 'current'
- });
- return false;
- });
- };
- if (value && !(value instanceof Array)) {
- new openerp.web.DataSet(
- this, this.field.relation, self.build_context())
- .name_get([value], function(data) {
- real_set_value(data[0]);
- });
- } else {
- $.async_when().then(function() {real_set_value(value);});
- }
- },
- get_value: function() {
- if (!this.value) {
- return false;
- } else if (this.value instanceof Array) {
- return this.value[0];
- } else {
- return this.value;
- }
- }
- });
- openerp.web.page.FieldReferenceReadonly = openerp.web.page.FieldMany2OneReadonly.extend({
- set_value: function (value) {
- if (!value) {
- return this._super(null);
- }
- var reference = value.split(',');
- this.field.relation = reference[0];
- var id = parseInt(reference[1], 10);
- return this._super(id);
- },
- get_value: function () {
- if (!this.value) {
- return null;
- }
- var id;
- if (typeof this.value === 'number') {
- // name_get has not run yet
- id = this.value;
- } else {
- id = this.value[0];
- }
- return _.str.sprintf('%s,%d', this.field.relation, id);
- }
- });
-
- openerp.web.page.FieldMany2ManyReadonly = openerp.web.form.FieldMany2Many.extend({
- force_readonly: true
- });
- openerp.web.page.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({
- force_readonly: true
- });
- openerp.web.page.FieldBinaryImageReaonly = openerp.web.form.FieldBinaryImage.extend({
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('.oe-binary').hide();
- }
- });
- openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({
- form_template: 'FieldURI.readonly',
- start: function() {
- this._super.apply(this, arguments);
- var self = this;
- this.$element.find('a').click(function() {
- if (self.value) {
- self.on_save_as();
- }
- return false;
- });
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.$element.find('a').show(!!value);
- if (value) {
- var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
- this.$element.find('a').text(show_value);
- }
- }
- });
- openerp.web.page.readonly = openerp.web.form.widgets.extend({
- 'frame': 'openerp.web.page.WidgetFrameReadonly',
- 'char': 'openerp.web.page.FieldCharReadonly',
- 'id': 'openerp.web.page.FieldCharReadonly',
- 'email': 'openerp.web.page.FieldEmailReadonly',
- 'url': 'openerp.web.page.FieldUrlReadonly',
- 'text': 'openerp.web.page.FieldCharReadonly',
- 'date': 'openerp.web.page.FieldCharReadonly',
- 'datetime': 'openerp.web.page.FieldCharReadonly',
- 'selection' : 'openerp.web.page.FieldSelectionReadonly',
- 'many2one': 'openerp.web.page.FieldMany2OneReadonly',
- 'many2many' : 'openerp.web.page.FieldMany2ManyReadonly',
- 'one2many' : 'openerp.web.page.FieldOne2ManyReadonly',
- 'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly',
- 'reference': 'openerp.web.page.FieldReferenceReadonly',
- 'boolean': 'openerp.web.page.FieldBooleanReadonly',
- 'float': 'openerp.web.page.FieldFloatReadonly',
- 'integer': 'openerp.web.page.FieldCharReadonly',
- 'float_time': 'openerp.web.page.FieldCharReadonly',
- 'binary': 'openerp.web.page.FieldBinaryFileReadonly',
- 'image': 'openerp.web.page.FieldBinaryImageReaonly'
- });
-};
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 6039e4a0822..9e938fe293a 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -2,12 +2,12 @@
* OpenERP web library
*---------------------------------------------------------*/
-openerp.web.view_tree = function(openerp) {
-var QWeb = openerp.web.qweb,
- _lt = openerp.web._lt;
+openerp.web.view_tree = function(instance) {
+var QWeb = instance.web.qweb,
+ _lt = instance.web._lt;
-openerp.web.views.add('tree', 'openerp.web.TreeView');
-openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# */{
+instance.web.views.add('tree', 'instance.web.TreeView');
+instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeView# */{
display_name: _lt('Tree'),
/**
* Indicates that this view is not searchable, and thus that no search
@@ -17,8 +17,8 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
/**
* Genuine tree view (the one displayed as a tree, not the list)
*
- * @constructs openerp.web.TreeView
- * @extends openerp.web.View
+ * @constructs instance.web.TreeView
+ * @extends instance.web.View
*
* @param parent
* @param dataset
@@ -39,7 +39,6 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
},
start: function () {
- this._super();
return this.rpc("/web/treeview/load", {
model: this.model,
view_id: this.view_id,
@@ -204,7 +203,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
'fields_view': self.fields_view.arch.children,
'fields': self.fields,
'level': $curr_node.data('level') || 0,
- 'render': openerp.web.format_value,
+ 'render': instance.web.format_value,
'color_for': self.color_for
});
@@ -227,12 +226,12 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
return this.rpc('/web/treeview/action', {
id: id,
model: this.dataset.model,
- context: new openerp.web.CompoundContext(
+ context: new instance.web.CompoundContext(
this.dataset.get_context(), local_context)
}).pipe(function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
- var c = new openerp.web.CompoundContext(local_context);
+ var c = new instance.web.CompoundContext(local_context);
if (action.context) {
c.add(action.context);
}
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 43ecc8444fc..36e3c2b802b 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -2,21 +2,11 @@
* OpenERP web library
*---------------------------------------------------------*/
-openerp.web.views = function(session) {
-var QWeb = session.web.qweb,
- _t = session.web._t;
+openerp.web.views = function(instance) {
+var QWeb = instance.web.qweb,
+ _t = instance.web._t;
-/**
- * Registry for all the client actions key: tag value: widget
- */
-session.web.client_actions = new session.web.Registry();
-
-/**
- * Registry for all the main views
- */
-session.web.views = new session.web.Registry();
-
-session.web.ActionManager = session.web.OldWidget.extend({
+instance.web.ActionManager = instance.web.Widget.extend({
init: function(parent) {
this._super(parent);
this.inner_action = null;
@@ -25,9 +15,6 @@ session.web.ActionManager = session.web.OldWidget.extend({
this.dialog_viewmanager = null;
this.client_widget = null;
},
- render: function() {
- return '<div id="' + this.element_id + '" style="height: 100%;"></div>';
- },
dialog_stop: function () {
if (this.dialog) {
this.dialog_viewmanager.destroy();
@@ -65,8 +52,8 @@ session.web.ActionManager = session.web.OldWidget.extend({
if (run_action) {
this.null_action();
action_loaded = this.do_action(state.action_id);
- session.webclient.menu.has_been_loaded.then(function() {
- session.webclient.menu.open_action(state.action_id);
+ instance.webclient.menu.has_been_loaded.then(function() {
+ instance.webclient.menu.open_action(state.action_id);
});
}
} else if (state.model && state.id) {
@@ -76,7 +63,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
res_model: state.model,
res_id: state.id,
type: 'ir.actions.act_window',
- views: [[false, 'page'], [false, 'form']]
+ views: [[false, 'form']]
};
action_loaded = this.do_action(action);
} else if (state.sa) {
@@ -112,12 +99,13 @@ session.web.ActionManager = session.web.OldWidget.extend({
}
var type = action.type.replace(/\./g,'_');
var popup = action.target === 'new';
+ var inline = action.target === 'inline';
action.flags = _.extend({
- views_switcher : !popup,
- search_view : !popup,
- action_buttons : !popup,
- sidebar : !popup,
- pager : !popup,
+ views_switcher : !popup && !inline,
+ search_view : !popup && !inline,
+ action_buttons : !popup && !inline,
+ sidebar : !popup && !inline,
+ pager : !popup && !inline,
display_title : !popup
}, action.flags || {});
if (!(type in this)) {
@@ -136,31 +124,31 @@ session.web.ActionManager = session.web.OldWidget.extend({
.contains(action.res_model)) {
var old_close = on_close;
on_close = function () {
- session.webclient.do_reload().then(old_close);
+ instance.webclient.do_reload().then(old_close);
};
}
if (action.target === 'new') {
if (this.dialog == null) {
- this.dialog = new session.web.Dialog(this, { width: '80%' });
+ this.dialog = new instance.web.Dialog(this, { width: '80%' });
if(on_close)
this.dialog.on_close.add(on_close);
} else {
this.dialog_viewmanager.destroy();
}
this.dialog.dialog_title = action.name;
- this.dialog_viewmanager = new session.web.ViewManagerAction(this, action);
+ this.dialog_viewmanager = new instance.web.ViewManagerAction(this, action);
this.dialog_viewmanager.appendTo(this.dialog.$element);
this.dialog.open();
} else {
if(action.menu_id) {
return this.getParent().do_action(action, function () {
- session.webclient.menu.open_menu(action.menu_id);
+ instance.webclient.menu.open_menu(action.menu_id);
});
}
this.dialog_stop();
this.content_stop();
this.inner_action = action;
- this.inner_viewmanager = new session.web.ViewManagerAction(this, action);
+ this.inner_viewmanager = new instance.web.ViewManagerAction(this, action);
this.inner_viewmanager.appendTo(this.$element);
}
},
@@ -182,7 +170,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
ir_actions_client: function (action) {
this.content_stop();
this.dialog_stop();
- var ClientWidget = session.web.client_actions.get_object(action.tag);
+ var ClientWidget = instance.web.client_actions.get_object(action.tag);
(this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element);
},
ir_actions_report_xml: function(action, on_closed) {
@@ -204,7 +192,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
}
self.dialog_stop();
},
- error: session.webclient.crashmanager.on_rpc_error
+ error: instance.webclient.crashmanager.on_rpc_error
})
});
},
@@ -216,16 +204,8 @@ session.web.ActionManager = session.web.OldWidget.extend({
}
});
-session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.ViewManager# */{
+instance.web.ViewManager = instance.web.Widget.extend({
template: "ViewManager",
- /**
- * @constructs session.web.ViewManager
- * @extends session.web.OldWidget
- *
- * @param parent
- * @param dataset
- * @param views
- */
init: function(parent, dataset, views, flags) {
this._super(parent);
this.model = dataset ? dataset.model : undefined;
@@ -234,7 +214,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
this.active_view = null;
this.views_src = _.map(views, function(x) {
if (x instanceof Array) {
- var View = session.web.views.get_object(x[1], true);
+ var View = instance.web.views.get_object(x[1], true);
return {
view_id: x[0],
view_type: x[1],
@@ -246,22 +226,16 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
});
this.views = {};
this.flags = flags || {};
- this.registry = session.web.views;
+ this.registry = instance.web.views;
this.views_history = [];
},
- render: function() {
- return session.web.qweb.render(this.template, {
- self: this,
- prefix: this.element_id,
- views: this.views_src});
- },
/**
* @returns {jQuery.Deferred} initial view loading promise
*/
start: function() {
this._super();
var self = this;
- this.$element.find('.oe_vm_switch button').click(function() {
+ this.$element.find('.oe_view_manager_switch a').click(function() {
self.on_mode_switch($(this).data('view-type'));
});
var views_ids = {};
@@ -270,7 +244,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
deferred : $.Deferred(),
controller : null,
options : _.extend({
- sidebar_id : self.element_id + '_sidebar_' + view.view_type,
+ $buttons : self.$element.find('.oe_view_manager_buttons'),
+ $sidebar : self.$element.find('.oe_view_manager_sidebar'),
+ $pager : self.$element.find('.oe_view_manager_pager'),
action : self.action,
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
@@ -278,7 +254,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
- this.$element.find('.oe_vm_switch').hide();
+ this.$element.find('.oe_view_manager_switch').hide();
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
@@ -292,9 +268,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
* @returns {jQuery.Deferred} new view loading promise
*/
on_mode_switch: function(view_type, no_store) {
- var self = this,
- view = this.views[view_type],
- view_promise;
+ var self = this;
+ var view = this.views[view_type];
+ var view_promise;
if(!view)
return $.Deferred().reject();
@@ -306,13 +282,22 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
if (!view.controller) {
// Lazy loading of views
var controllerclass = this.registry.get_object(view_type);
- var controller = new controllerclass(this, this.dataset, view.view_id, view.options);
+ var options = _.clone(view.options);
+ if (view_type === "form" && this.action) {
+ switch (this.action.target) {
+ case 'new':
+ case 'inline':
+ options.initial_mode = 'edit';
+ break;
+ }
+ }
+ var controller = new controllerclass(this, this.dataset, view.view_id, options);
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
controller.do_switch_view.add_last(this.on_mode_switch);
controller.do_prev_view.add_last(this.on_prev_view);
- var container = $("#" + this.element_id + '_view_' + view_type);
+ var container = this.$element.find(".oe_view_manager_view_" + view_type);
view_promise = controller.appendTo(container);
this.views[view_type].controller = controller;
this.views[view_type].deferred.resolve(view_type);
@@ -335,9 +320,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
}
this.$element
- .find('.oe_vm_switch button').removeAttr('disabled')
- .filter('[data-view-type="' + view_type + '"]')
- .attr('disabled', true);
+ .find('.oe_view_manager_switch a').parent().removeClass('active')
+ this.$element
+ .find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
+ .parent().addClass('active');
$.when(view_promise).then(function () {
_.each(_.keys(self.views), function(view_name) {
@@ -367,15 +353,16 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
* @returns {$.Deferred} switching end signal
*/
on_prev_view: function (options) {
+ options = options || {};
var current_view = this.views_history.pop();
var previous_view = this.views_history[this.views_history.length - 1] || options['default'];
if (options.created && current_view === 'form' && previous_view === 'list') {
// APR special case: "If creation mode from list (and only from a list),
// after saving, go to page view (don't come back in list)"
- return this.on_mode_switch('page');
+ return this.on_mode_switch('form');
} else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
// APR special case: "If creation from dashboard, we have no previous view
- return this.on_mode_switch('page');
+ return this.on_mode_switch('form');
}
return this.on_mode_switch(previous_view, true);
},
@@ -390,12 +377,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
if (this.searchview) {
this.searchview.destroy();
}
- this.searchview = new session.web.SearchView(
- this, this.dataset,
- view_id, search_defaults, this.flags.search_view === false);
+ this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
this.searchview.on_search.add(this.do_searchview_search);
- return this.searchview.appendTo($("#" + this.element_id + "_search"));
+ return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
var self = this,
@@ -451,13 +436,13 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
}
});
-session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{
+instance.web.ViewManagerAction = instance.web.ViewManager.extend({
template:"ViewManagerAction",
/**
- * @constructs session.web.ViewManagerAction
- * @extends session.web.ViewManager
+ * @constructs instance.web.ViewManagerAction
+ * @extends instance.web.ViewManager
*
- * @param {session.web.ActionManager} parent parent object/widget
+ * @param {instance.web.ActionManager} parent parent object/widget
* @param {Object} action descriptor for the action this viewmanager needs to manage its views.
*/
init: function(parent, action) {
@@ -482,7 +467,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
this._super(parent, null, action.views, flags);
this.session = parent.session;
this.action = action;
- var dataset = new session.web.DataSetSearch(this, action.res_model, action.context, action.domain);
+ var dataset = new instance.web.DataSetSearch(this, action.res_model, action.context, action.domain);
if (action.res_id) {
dataset.ids.push(action.res_id);
dataset.index = 0;
@@ -523,7 +508,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
this.$element.find('.oe_debug_view').change(this.on_debug_changed);
if (this.action.help && !this.flags.low_profile) {
- var Users = new session.web.DataSet(self, 'res.users'),
+ var Users = new instance.web.DataSet(self, 'res.users'),
$tips = this.$element.find('.oe_view_manager_menu_tips');
$tips.delegate('blockquote button', 'click', function() {
var $this = $(this);
@@ -547,17 +532,6 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
}
}
- var $res_logs = this.$element.find('.oe-view-manager-logs:first');
- $res_logs.delegate('a.oe-more-logs', 'click', function () {
- $res_logs.removeClass('oe-folded');
- return false;
- }).delegate('a.oe-remove-everything', 'click', function () {
- $res_logs.removeClass('oe-has-more').find('ul').empty();
- $res_logs.css('display','none');
- return false;
- });
- $res_logs.css('display','none');
-
return manager_ready;
},
on_debug_changed: function (evt) {
@@ -568,23 +542,26 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
current_view = this.views[this.active_view].controller;
switch (val) {
case 'fvg':
- var dialog = new session.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
- $('<pre>').text(session.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element);
+ var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
+ $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element);
break;
case 'perm_read':
var ids = current_view.get_selected_ids();
if (ids.length === 1) {
this.dataset.call('perm_read', [ids]).then(function(result) {
- var dialog = new session.web.Dialog(this, {
+ var dialog = new instance.web.Dialog(this, {
title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model),
width: 400
}, QWeb.render('ViewManagerDebugViewLog', {
perm : result[0],
- format : session.web.format_value
+ format : instance.web.format_value
})).open();
});
}
break;
+ case 'toggle_layout_outline':
+ current_view.rendering_engine.toggle_layout_debugging();
+ break;
case 'fields':
this.dataset.call_and_eval(
'fields_get', [false, {}], null, 1).then(function (fields) {
@@ -602,7 +579,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
.append($('<dd style="white-space: pre-wrap;">').text(def));
});
});
- new session.web.Dialog(self, {
+ new instance.web.Dialog(self, {
title: _.str.sprintf(_t("Model %s fields"),
self.dataset.model),
width: '95%'}, $root).open();
@@ -610,7 +587,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
break;
case 'manage_views':
if (current_view.fields_view && current_view.fields_view.arch) {
- var view_editor = new session.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch);
+ var view_editor = new instance.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch);
view_editor.start();
} else {
this.do_warn(_t("Manage Views"),
@@ -661,8 +638,6 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
return $.when(this._super(view_type, no_store)).then(function () {
self.shortcut_check(self.views[view_type]);
- self.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').removeClass('oe-has-more').css('display','none').find('ul').empty();
-
var controller = self.views[self.active_view].controller,
fvg = controller.fields_view,
view_id = (fvg && fvg.view_id) || '--';
@@ -744,127 +719,88 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
}
});
},
- /**
- * Intercept do_action resolution from children views
- */
- on_action_executed: function () {
- return new session.web.DataSet(this, 'res.log')
- .call('get', [], this.do_display_log);
- },
- /**
- * @param {Array<Object>} log_records
- */
- do_display_log: function (log_records) {
- var self = this;
- var cutoff = 3;
- var $logs = this.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').css('display', 'block');
- var $logs_list = $logs.find('ul').empty();
- $logs.toggleClass('oe-has-more', log_records.length > cutoff);
- _(log_records.reverse()).each(function (record) {
- var context = {};
- if (record.context) {
- try { context = py.eval(record.context); }
- catch (e) { /* TODO: what do I do now? */ }
- }
- $(_.str.sprintf('<li><a href="#">%s</a></li>', record.name))
- .appendTo($logs_list)
- .delegate('a', 'click', function () {
- self.do_action({
- type: 'ir.actions.act_window',
- res_model: record.res_model,
- res_id: record.res_id,
- // TODO: need to have an evaluated context here somehow
- context: context,
- views: [[context.view_id || false, 'form']]
- });
- return false;
- });
- });
- },
display_title: function () {
return this.action.name;
}
});
-session.web.Sidebar = session.web.OldWidget.extend({
- init: function(parent, element_id) {
- this._super(parent, element_id);
- this.items = {};
- this.sections = {};
+instance.web.Sidebar = instance.web.Widget.extend({
+ init: function(parent) {
+ this._super(parent);
+ var view = this.getParent();
+ this.sections = [
+ { 'name' : 'print', 'label' : _t('Print'), },
+ { 'name' : 'files', 'label' : _t('Attachement'), },
+ { 'name' : 'other', 'label' : _t('More'), }
+ ];
+ this.items = {
+ 'print' : [],
+ 'files' : [],
+ 'other' : [
+ { label: _t("Import"), callback: view.on_sidebar_import },
+ { label: _t("Export"), callback: view.on_sidebar_export }
+ ]
+ }
+ if (this.session.uid === 1) {
+ var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") };
+ this.items.other.push(item);
+ }
},
start: function() {
- this._super(this);
var self = this;
- this.$element.html(session.web.qweb.render('Sidebar'));
- this.$element.find(".toggle-sidebar").click(function(e) {
- self.do_toggle();
+ this._super(this);
+ this.redraw();
+ this.$element.on('click','.oe_dropdown_toggle',function(event) {
+ $(this).parent().find('ul').toggle();
+ return false;
});
- },
- add_default_sections: function() {
- var self = this,
- view = this.getParent(),
- view_manager = view.getParent(),
- action = view_manager.action;
- if (this.session.uid === 1) {
- this.add_section(_t('Customize'), 'customize');
- this.add_items('customize', [{
- label: _t("Translate"),
- callback: view.on_sidebar_translate,
- title: _t("Technical translation")
- }]);
- }
-
- this.add_section(_t('Other Options'), 'other');
- this.add_items('other', [
- {
- label: _t("Import"),
- callback: view.on_sidebar_import
- }, {
- label: _t("Export"),
- callback: view.on_sidebar_export
+ this.$element.on('click','.oe_dropdown_menu li a', function(event) {
+ var section = $(this).data('section');
+ var index = $(this).data('index');
+ $(this).closest('ul').hide();
+ var item = self.items[section][index];
+ if (item.callback) {
+ item.callback.apply(self, [item]);
+ } else if (item.action) {
+ self.on_item_action_clicked(item);
+ } else if (item.url) {
+ return true;
}
- ]);
+ return false;
+ });
+ //this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
+ //this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
+ //this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
+ },
+ redraw: function() {
+ var self = this;
+ self.$element.html(QWeb.render('Sidebar', {widget: self}));
+ this.$element.find('ul').hide();
+ },
+ add_section: function() {
+ var self = this;
},
-
add_toolbar: function(toolbar) {
var self = this;
- _.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) {
- var items = toolbar[type[0]];
- if (items.length) {
+ _.each(['print','action','relate'], function(type) {
+ var items = toolbar[type];
+ if (items) {
for (var i = 0; i < items.length; i++) {
items[i] = {
label: items[i]['name'],
action: items[i],
- classname: 'oe_sidebar_' + type[0]
+ classname: 'oe_sidebar_' + type
}
}
- self.add_section(type[1], type[0]);
- self.add_items(type[0], items);
+ self.add_items(type=='print' ? 'print' : 'other', items);
}
});
},
-
- add_section: function(name, code) {
- if(!code) code = _.str.underscored(name);
- var $section = this.sections[code];
-
- if(!$section) {
- var section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
- $section = $(session.web.qweb.render("Sidebar.section", {
- section_id: section_id,
- name: name,
- classname: 'oe_sidebar_' + code
- }));
- $section.appendTo(this.$element.find('div.sidebar-actions'));
- this.sections[code] = $section;
- }
- return $section;
- },
/**
* For each item added to the section:
*
* ``label``
- * will be used as the item's name in the sidebar
+ * will be used as the item's name in the sidebar, can be html
*
* ``action``
* descriptor for the action which will be executed, ``action`` and
@@ -886,34 +822,10 @@ session.web.Sidebar = session.web.OldWidget.extend({
* @param {Array<{label, action | callback[, classname][, title]}>} items
*/
add_items: function(section_code, items) {
- var self = this,
- $section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code),
- section_id = $section.attr('id');
-
+ var self = this;
if (items) {
- for (var i = 0; i < items.length; i++) {
- items[i].element_id = _.uniqueId(section_id + '_item_');
- this.items[items[i].element_id] = items[i];
- }
-
- var $items = $(session.web.qweb.render("Sidebar.section.items", {items: items}));
-
- $items.find('a.oe_sidebar_action_a').click(function() {
- var item = self.items[$(this).attr('id')];
- if (item.callback) {
- item.callback.apply(self, [item]);
- }
- if (item.action) {
- self.on_item_action_clicked(item);
- }
- return false;
- });
-
- var $ul = $section.find('ul');
- if(!$ul.length) {
- $ul = $('<ul/>').appendTo($section);
- }
- $items.appendTo($ul);
+ this.items[section_code].push.apply(this.items[section_code],items);
+ this.redraw();
}
},
on_item_action_clicked: function(item) {
@@ -921,11 +833,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
self.getParent().sidebar_context().then(function (context) {
var ids = self.getParent().get_selected_ids();
if (ids.length == 0) {
- //TODO: make prettier warning?
- openerp.web.dialog($("<div />").text(_t("You must choose at least one record.")), {
- title: _t("Warning"),
- modal: true
- });
+ instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
return false;
}
var additional_context = _.extend({
@@ -948,18 +856,60 @@ session.web.Sidebar = session.web.OldWidget.extend({
});
});
},
- do_fold: function() {
- this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
+ do_attachement_update: function(dataset, model_id) {
+ if (!model_id) {
+ this.on_attachments_loaded([]);
+ } else {
+ var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ];
+ var ds = new instance.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom);
+ ds.read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
+ }
},
- do_unfold: function() {
- this.$element.addClass('open-sidebar').removeClass('closed-sidebar');
+ on_attachments_loaded: function(attachments) {
+ var self = this;
+ var items = [];
+ // TODO: preprend: _s +
+ var prefix = '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id=';
+ _.each(attachments,function(a) {
+ a.label = a.name;
+ if(a.type === "binary") {
+ a.url = prefix + a.id + '&t=' + (new Date().getTime());
+ }
+ });
+ attachments.push( { label: _t("Add..."), callback: self.on_attachment_add } );
+ self.items['files'] = attachments;
+ self.redraw();
+ },
+ on_attachment_add: function(e) {
+ this.$element.find('.oe_sidebar_add').show();
+ },
+ on_attachment_changed: function(e) {
+ return;
+ window[this.element_id + '_iframe'] = this.do_update;
+ var $e = $(e.target);
+ if ($e.val() != '') {
+ this.$element.find('form.oe-binary-form').submit();
+ $e.parent().find('input[type=file]').prop('disabled', true);
+ $e.parent().find('button').prop('disabled', true).find('img, span').toggle();
+ }
},
- do_toggle: function() {
- this.$element.toggleClass('open-sidebar closed-sidebar');
+ on_attachment_delete: function(e) {
+ return;
+ var self = this, $e = $(e.currentTarget);
+ var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
+ if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) {
+ this.rpc('/web/dataset/unlink', {
+ model: 'ir.attachment',
+ ids: [parseInt($e.attr('data-id'))]
+ }, function(r) {
+ $e.parent().remove();
+ self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
+ });
+ }
}
});
-session.web.TranslateDialog = session.web.Dialog.extend({
+instance.web.TranslateDialog = instance.web.Dialog.extend({
dialog_title: {toString: function () { return _t("Translations"); }},
init: function(view) {
// TODO fme: should add the language to fields_view_get because between the fields view get
@@ -979,14 +929,14 @@ session.web.TranslateDialog = session.web.Dialog.extend({
this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name });
this.languages = null;
this.languages_loaded = $.Deferred();
- (new session.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
+ (new instance.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
[['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded);
},
start: function() {
var self = this;
this._super();
$.when(this.languages_loaded).then(function() {
- self.$element.html(session.web.qweb.render('TranslateDialog', { widget: self }));
+ self.$element.html(instance.web.qweb.render('TranslateDialog', { widget: self }));
self.$fields_form = self.$element.find('.oe_translation_form');
self.$fields_form.find('.oe_trad_field').change(function() {
$(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value')));
@@ -1075,29 +1025,61 @@ session.web.TranslateDialog = session.web.Dialog.extend({
}
});
-session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
+instance.web.View = instance.web.Widget.extend({
template: "EmptyComponent",
// name displayed in view switchers
display_name: '',
+ /**
+ * Define a view type for each view to allow automatic call to fields_view_get.
+ */
+ view_type: undefined,
init: function(parent, dataset, view_id, options) {
this._super(parent);
this.dataset = dataset;
this.view_id = view_id;
this.set_default_options(options);
},
+ start: function() {
+ return this.load_view();
+ },
+ load_view: function() {
+ if (this.embedded_view) {
+ var def = $.Deferred();
+ var self = this;
+ $.async_when().then(function() {def.resolve(self.embedded_view);});
+ return def.pipe(this.on_loaded);
+ } else {
+ var context = new instance.web.CompoundContext(this.dataset.get_context());
+ if (! this.view_type)
+ console.warn("view_type is not defined", this);
+ return this.rpc("/web/view/load", {
+ "model": this.dataset.model,
+ "view_id": this.view_id,
+ "view_type": this.view_type,
+ toolbar: !!this.options.$sidebar,
+ context: context
+ }).pipe(this.on_loaded);
+ }
+ },
+ /**
+ * Called after a successful call to fields_view_get.
+ * Must return a promise.
+ */
+ on_loaded: function(fields_view_get) {
+ },
set_default_options: function(options) {
this.options = options || {};
_.defaults(this.options, {
// All possible views options should be defaulted here
+ $sidebar: null,
sidebar_id: null,
- sidebar: true,
action: null,
action_views_ids: {}
});
},
open_translate_dialog: function(field) {
if (!this.translate_dialog) {
- this.translate_dialog = new session.web.TranslateDialog(this).start();
+ this.translate_dialog = new instance.web.TranslateDialog(this).start();
}
this.translate_dialog.open(field);
},
@@ -1109,7 +1091,7 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
* @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
* @param {Object} [action_data.context=null] additional action context, to add to the current context
- * @param {session.web.DataSet} dataset a dataset object used to communicate with the server
+ * @param {instance.web.DataSet} dataset a dataset object used to communicate with the server
* @param {Object} [record_id] the identifier of the object on which the action is to be applied
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
*/
@@ -1121,12 +1103,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
return self.getParent().on_action_executed.apply(null, arguments);
}
};
- var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {});
+ var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
var handler = function (r) {
var action = r.result;
if (action && action.constructor == Object) {
- var ncontext = new session.web.CompoundContext(context);
+ var ncontext = new instance.web.CompoundContext(context);
if (record_id) {
ncontext.add({
active_id: record_id,
@@ -1175,13 +1157,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
/**
* Directly set a view to use instead of calling fields_view_get. This method must
* be called before start(). When an embedded view is set, underlying implementations
- * of session.web.View must use the provided view instead of any other one.
+ * of instance.web.View must use the provided view instead of any other one.
*
* @param embedded_view A view.
*/
set_embedded_view: function(embedded_view) {
this.embedded_view = embedded_view;
- this.options.sidebar = false;
},
do_show: function () {
this.$element.show();
@@ -1214,15 +1195,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
},
do_search: function(view) {
},
- set_common_sidebar_sections: function(sidebar) {
- sidebar.add_default_sections();
- },
on_sidebar_import: function() {
- var import_view = new session.web.DataImport(this, this.dataset);
+ var import_view = new instance.web.DataImport(this, this.dataset);
import_view.start();
},
on_sidebar_export: function() {
- var export_view = new session.web.DataExport(this, this.dataset);
+ var export_view = new instance.web.DataExport(this, this.dataset);
export_view.start();
},
on_sidebar_translate: function() {
@@ -1247,7 +1225,29 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
}
});
-session.web.json_node_to_xml = function(node, human_readable, indent) {
+instance.web.xml_to_json = function(node) {
+ switch (node.nodeType) {
+ case 3:
+ case 4:
+ return node.data;
+ break;
+ case 1:
+ var attrs = $(node).getAttributes();
+ _.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) {
+ if (attrs[key]) {
+ try {
+ attrs[key] = JSON.parse(attrs[key]);
+ } catch(e) { }
+ }
+ });
+ return {
+ tag: node.tagName.toLowerCase(),
+ attrs: attrs,
+ children: _.map(node.childNodes, instance.web.xml_to_json)
+ }
+ }
+}
+instance.web.json_node_to_xml = function(node, human_readable, indent) {
// For debugging purpose, this function will convert a json node back to xml
// Maybe useful for xml view editor
indent = indent || 0;
@@ -1276,7 +1276,7 @@ session.web.json_node_to_xml = function(node, human_readable, indent) {
r += '>' + cr;
var childs = [];
for (var i = 0, ii = node.children.length; i < ii; i++) {
- childs.push(session.web.json_node_to_xml(node.children[i], human_readable, indent + 1));
+ childs.push(instance.web.json_node_to_xml(node.children[i], human_readable, indent + 1));
}
r += childs.join(cr);
r += cr + sindent + '</' + node.tag + '>';
@@ -1285,6 +1285,43 @@ session.web.json_node_to_xml = function(node, human_readable, indent) {
return r + '/>';
}
}
+instance.web.xml_to_str = function(node) {
+ if (window.ActiveXObject) {
+ return node.xml;
+ } else {
+ return (new XMLSerializer()).serializeToString(node);
+ }
+}
+instance.web.str_to_xml = function(s) {
+ if (window.DOMParser) {
+ var dp = new DOMParser();
+ var r = dp.parseFromString(s, "text/xml");
+ if (r.body && r.body.firstChild && r.body.firstChild.nodeName == 'parsererror') {
+ throw new Error("Could not parse string to xml");
+ }
+ return r;
+ }
+ var xDoc;
+ try {
+ xDoc = new ActiveXObject("MSXML2.DOMDocument");
+ } catch (e) {
+ throw new Error("Could not find a DOM Parser: " + e.message);
+ }
+ xDoc.async = false;
+ xDoc.preserveWhiteSpace = true;
+ xDoc.loadXML(s);
+ return xDoc;
+}
+
+/**
+ * Registry for all the client actions key: tag value: widget
+ */
+instance.web.client_actions = new instance.web.Registry();
+
+/**
+ * Registry for all the main views
+ */
+instance.web.views = new instance.web.Registry();
};
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 894f7353c1a..0e1edb89969 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -2,6 +2,14 @@
<!-- vim:fdl=1:
-->
<templates id="template" xml:space="preserve">
+<t t-name="EmptyComponent">
+ <div></div>
+</t>
+<t t-name="Loading">
+ <div class="oe_loading">
+ Loading...
+ </div>
+</t>
<t t-name="Notification">
<div class="oe_notification" t-translation="off">
<div id="oe_notification_default">
@@ -17,208 +25,8 @@
</div>
</div>
</t>
-<t t-name="WebClient">
- <div class="oe_webclient">
- <div class="oe_topbar">
- <div class="oe_menu_placeholder"/>
- <div class="oe_user_menu_placeholder"/>
- <div class="oe_systray"/>
- </div>
- <div class="oe_leftbar">
- <a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
- <div class="oe_secondary_menus_container"/>
-
- <div class="oe_footer">
- Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
- </div>
- </div>
- <div class="oe_application"/>
- </div>
-</t>
-<t t-name="Loading">
- <div id="oe_loading">
- <div class="loading">
- Loading...
- </div>
- </div>
-</t>
-<t t-name="Database.CreateDB">
- <form name="create_db_form" class="oe_forms" method="POST">
- <table width="100%">
- <tr>
- <td class="option_string">
- CREATE DATABASE
- </td>
- </tr>
- </table>
- <table align="center" class="db_option_table">
- <tr>
- <td><label for="super_admin_pwd">Master password:</label></td>
- <td><input type="password" name="super_admin_pwd" class="required" value="admin"/></td>
- </tr>
- <tr>
- <td><label for="db_name">New database name:</label></td>
- <td><input type="text" name="db_name"
- class="required" matches="^[a-zA-Z][a-zA-Z0-9_]+$"/></td>
- </tr>
- <tr>
- <td><label for="demo_data">Load Demonstration data:</label></td>
- <td><input type="checkbox" name="demo_data"/></td>
- </tr>
- <tr>
- <td><label for="db_lang">Default language:</label></td>
- <td>
- <select name="db_lang" t-if="lang_list">
- <t t-foreach="lang_list" t-as="lang">
- <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined"><t t-esc="lang[1]"/></option>
- </t>
- </select>
- </td>
- </tr>
- <tr>
- <td><label for="create_admin_pwd">Admin password:</label></td>
- <td><input type="password" name="create_admin_pwd" class="required"/></td>
- </tr>
- <tr>
- <td><label for="create_confirm_pwd">Confirm password:</label></td>
- <td><input type="password" name="create_confirm_pwd" class="required"
- equalTo="input[name=create_admin_pwd]"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Create</button></td>
- </tr>
- </table>
- </form>
-</t>
-<t t-name="DropDB">
- <form name="drop_db_form" class="oe_forms" method="POST">
- <table width="100%">
- <tr>
- <td class="option_string">
- DROP DATABASE
- </td>
- </tr>
- </table>
- <table align="center" class="db_option_table">
- <tr>
- <td><label for="drop_db">Database:</label></td>
- <td>
- <select t-if="db_list" name="drop_db" autofocus="autofocus">
- <t t-foreach="db_list" t-as="db">
- <option t-att-value="db"><t t-esc="db"/></option>
- </t>
- </select>
- <input t-if="!db_list" name="drop_db" class="required"
- type="text" autofocus="autofocus"/>
- </td>
- </tr>
- <tr>
- <td><label for="drop_password">Master Password:</label></td>
- <td><input type="password" name="drop_pwd" class="required"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Drop</button></td>
- </tr>
- </table>
- </form>
-</t>
-<t t-name="BackupDB">
- <form name="backup_db_form" class="oe_forms" method="POST" target="backup-target"
- action="/web/database/backup">
- <input type="hidden" name="token"/>
- <table width="100%">
- <tr>
- <td class="option_string">
- BACKUP DATABASE
- </td>
- </tr>
- </table>
- <table align="center" class="db_option_table">
- <tr>
- <td><label for="backup_db">Database:</label></td>
- <td>
- <select t-if="db_list" name="backup_db" autofocus="autofocus">
- <t t-foreach="db_list" t-as="db">
- <option t-att-value="db"><t t-esc="db"/></option>
- </t>
- </select>
- <input t-if="!db_list" name="backup_db" class="required"
- type="text" autofocus="autofocus"/>
- </td>
- </tr>
- <tr>
- <td><label for="backup_pwd">Master Password:</label></td>
- <td><input type="password" name="backup_pwd" class="required"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Backup</button></td>
- </tr>
- </table>
- </form>
-</t>
-<t t-name="RestoreDB">
- <form name="restore_db_form" class="oe_forms" method="POST">
- <table width="100%">
- <tr>
- <td class="option_string">
- RESTORE DATABASE
- </td>
- </tr>
- </table>
- <table align="center" class="db_option_table">
- <tr>
- <td><label for="restore_db">File:</label></td>
- <td><input type="file" name="db_file" class="required"
- autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="restore_pwd">Master Password:</label></td>
- <td><input type="password" name="restore_pwd" class="required"/></td>
- </tr>
- <tr>
- <td><label for="new_db">New database name:</label></td>
- <td><input type="text" name="new_db" class="required"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Restore</button></td>
- </tr>
- </table>
- </form>
-</t>
-<t t-name="Change_DB_Pwd">
- <form name="change_pwd_form" class="oe_forms" method="POST">
- <table width="100%">
- <tr>
- <td class="option_string">
- CHANGE MASTER PASSWORD
- </td>
- </tr>
- </table>
- <table align="center" class="db_option_table">
- <tr>
- <td><label for="old_pwd">Master password:</label></td>
- <td><input type="password" name="old_pwd" class="required"
- minlength="1" autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="new_pwd">New master password:</label></td>
- <td><input type="password" name="new_pwd" class="required"
- minlength="1"/></td>
- </tr>
- <tr>
- <td><label for="confirm_pwd">Confirm new master password:</label></td>
- <td><input type="password" name="confirm_pwd" class="required"
- equalTo="input[name=new_pwd]" minlength="1"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
- </tr>
- </table>
- </form>
-</t>
-
-<t t-name="CrashManagerWarning">
+<t t-name="CrashManager.warning">
<table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
<tr>
<td><img t-att-src='_s + "/web/static/src/img/warning.png"' class="oe-dialog-icon"/></td>
@@ -235,7 +43,7 @@
</tr>
</table>
</t>
-<t t-name="CrashManagerError">
+<t t-name="CrashManager.error">
<t t-if="!session.openerp_entreprise">
<span>Your version of OpenERP is unsupported. Support &amp; maintenance services are available here: <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">OpenERP Entreprise</a>.</span>
</t>
@@ -266,7 +74,33 @@
</div>
</t>
-<t t-name="Login_dblist">
+<t t-name="Login">
+ <div class="oe_login">
+ <div class="oe_login_bottom"> </div>
+ <div class="oe_login_error_message">Invalid username or password</div>
+ <div class="oe_login_pane">
+ <div class="oe_login_logo"><img src='/web/static/src/img/logo2.png'/></div>
+ <form action="" method="post">
+ <div class="oe_login_dbpane" >
+ Database:
+ <input name="db" t-att-value="widget.selected_db || ''"/>
+ </div>
+ <ul>
+ <li>Username</li>
+ <li><input type="text" name="login" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li>
+ <li>Password</li>
+ <li><input type="password" name="password" t-att-value="widget.selected_password || ''"/></li>
+ <li><button name="submit">Log in</button></li>
+ </ul>
+ </form>
+ <div class="oe_login_footer">
+ <a href="#" class="oe_login_manage_db">Manage Databases</a> |
+ <a href="http://www.openerp.com">Powered by <span>OpenERP</span></a>
+ </div>
+ </div>
+ </div>
+</t>
+<t t-name="Login.dblist">
<select name="db">
<t t-foreach="db_list" t-as="db">
<t t-if="selected_db === db">
@@ -280,51 +114,179 @@
</select>
</t>
-<t t-name="Login">
- <div class="login">
- <div class="bottom"> </div>
- <div class="login_error_message">Invalid username or password</div>
- <div class="pane">
- <div id="logo"><img src='/web/static/src/img/logo2.png'/></div>
- <form action="" method="post">
- <div class="dbpane" >
- Database:
- <input name="db" t-att-value="widget.selected_db || ''"/>
- </div>
- <ul>
- <li>Username</li>
- <li><input type="text" name="login" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li>
- <li>Password</li>
- <li><input type="password" name="password" t-att-value="widget.selected_password || ''"/></li>
- <li><button name="submit">Log in</button></li>
- </ul>
- </form>
- <div class="footer">
- <a href="#" id="oe-db-config">Manage Databases</a> |
- <a href="http://www.openerp.com">Powered by <span class="openerp">OpenERP</span></a>
- </div>
- </div>
- </div>
-</t>
<t t-name="DatabaseManager">
- <div class="oe-database-manager">
- <div class="database">
- <a class="company_logo_link" href="/?">
- <div class="company_logo"></div>
- </a>
- <ul class="db_options">
- <li id="db-create">Create</li>
- <li id="db-drop">Drop</li>
- <li id="db-backup">Backup</li>
- <li id="db-restore">Restore</li>
- <li id="db-change-password">Password</li>
- <li id="back-to-login">Back to Login</li>
+ <div class="oe_database_manager">
+ <div class="oe_database_manager_menu">
+ <ul class="oe_form_notebook">
+ <li><a href="#db_create">Create</a></li>
+ <li><a href="#db_drop">Drop</a></li>
+ <li><a href="#db_backup">Backup</a></li>
+ <li><a href="#db_restore">Restore</a></li>
+ <li><a href="#db_change_password">Password</a></li>
+ <li><a id="back_to_login" href="#">Back to Login</a></li>
</ul>
+ <form id="db_create" name="create_db_form" class="oe_forms" method="POST">
+ <table align="center" class="db_option_table">
+ <tr>
+ <th colspan="2" class="option_string"> CREATE DATABASE </th>
+ </tr>
+ <tr>
+ <td><label for="super_admin_pwd">Master password:</label></td>
+ <td><input type="password" name="super_admin_pwd" class="required" value="admin"/></td>
+ </tr>
+ <tr>
+ <td><label for="db_name">New database name:</label></td>
+ <td><input type="text" name="db_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_]+$"/></td>
+ </tr>
+ <tr>
+ <td><label for="demo_data">Load Demonstration data:</label></td>
+ <td><input type="checkbox" name="demo_data"/></td>
+ </tr>
+ <tr>
+ <td><label for="db_lang">Default language:</label></td>
+ <td>
+ <select name="db_lang" t-if="widget.lang_list">
+ <t t-foreach="widget.lang_list" t-as="lang">
+ <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined"><t t-esc="lang[1]"/></option>
+ </t>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td><label for="create_admin_pwd">Admin password:</label></td>
+ <td><input type="password" name="create_admin_pwd" class="required"/></td>
+ </tr>
+ <tr>
+ <td><label for="create_confirm_pwd">Confirm password:</label></td>
+ <td><input type="password" name="create_confirm_pwd" class="required" equalTo="input[name=create_admin_pwd]"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Create</button></td>
+ </tr>
+ </table>
+ </form>
+ <form id="db_drop" name="drop_db_form" class="oe_forms" method="POST">
+ <table align="center" class="db_option_table">
+ <tr>
+ <th colspan="2" class="option_string"> DROP DATABASE </th>
+ </tr>
+ <tr>
+ <td><label for="drop_db">Database:</label></td>
+ <td>
+ <select t-if="widget.db_list" name="drop_db" autofocus="autofocus">
+ <t t-foreach="widget.db_list" t-as="db">
+ <option t-att-value="db"><t t-esc="db"/></option>
+ </t>
+ </select>
+ <input t-if="!widget.db_list" name="drop_db" class="required" type="text" autofocus="autofocus"/>
+ </td>
+ </tr>
+ <tr>
+ <td><label for="drop_password">Master Password:</label></td>
+ <td><input type="password" name="drop_pwd" class="required"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Drop</button></td>
+ </tr>
+ </table>
+ </form>
+ <form id="db_backup" name="backup_db_form" class="oe_forms" method="POST" target="backup-target" action="/web/database/backup">
+ <input type="hidden" name="token"/>
+ <table align="center" class="db_option_table">
+ <tr>
+ <th colspan="2" class="option_string"> BACKUP DATABASE </th>
+ </tr>
+ <tr>
+ <td><label for="backup_db">Database:</label></td>
+ <td>
+ <select t-if="widget.db_list" name="backup_db" autofocus="autofocus">
+ <t t-foreach="widget.db_list" t-as="db">
+ <option t-att-value="db"><t t-esc="db"/></option>
+ </t>
+ </select>
+ <input t-if="!widget.db_list" name="backup_db" class="required" type="text" autofocus="autofocus"/>
+ </td>
+ </tr>
+ <tr>
+ <td><label for="backup_pwd">Master Password:</label></td>
+ <td><input type="password" name="backup_pwd" class="required"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Backup</button></td>
+ </tr>
+ </table>
+ </form>
+ <form id="db_restore" name="restore_db_form" class="oe_forms" method="POST">
+ <table align="center" class="db_option_table">
+ <tr>
+ <th colspan="2" class="option_string"> RESTORE DATABASE </th>
+ </tr>
+ <tr>
+ <td><label for="restore_db">File:</label></td>
+ <td><input type="file" name="db_file" class="required" autofocus="autofocus"/></td>
+ </tr>
+ <tr>
+ <td><label for="restore_pwd">Master Password:</label></td>
+ <td><input type="password" name="restore_pwd" class="required"/></td>
+ </tr>
+ <tr>
+ <td><label for="new_db">New database name:</label></td>
+ <td><input type="text" name="new_db" class="required"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Restore</button></td>
+ </tr>
+ </table>
+ </form>
+ <form id="db_change_password" name="change_pwd_form" class="oe_forms" method="POST">
+ <table align="center" class="db_option_table">
+ <tr>
+ <th colspan="2" class="option_string"> CHANGE MASTER PASSWORD </th>
+ </tr>
+ <tr>
+ <td><label for="old_pwd">Master password:</label></td>
+ <td><input type="password" name="old_pwd" class="required" minlength="1" autofocus="autofocus"/></td>
+ </tr>
+ <tr>
+ <td><label for="new_pwd">New master password:</label></td>
+ <td><input type="password" name="new_pwd" class="required" minlength="1"/></td>
+ </tr>
+ <tr>
+ <td><label for="confirm_pwd">Confirm new master password:</label></td>
+ <td><input type="password" name="confirm_pwd" class="required" equalTo="input[name=new_pwd]" minlength="1"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
+ </tr>
+ </table>
+ </form>
</div>
- <div id="oe_db_options" class="oe_db_options"></div>
</div>
</t>
+<t t-name="WebClient">
+ <table class="oe_webclient">
+ <tr>
+ <td colspan="2" class="oe_topbar">
+ <div class="oe_menu_placeholder"/>
+ <div class="oe_user_menu_placeholder"/>
+ <div class="oe_systray"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="oe_leftbar" valign="top">
+ <a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
+ <div class="oe_secondary_menus_container"/>
+ <div class="oe_footer">
+ Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
+ </div>
+ </td>
+ <td class="oe_application">
+ </td>
+ </tr>
+ </table>
+</t>
+
<t t-name="Menu">
<ul class="oe_menu" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
@@ -362,12 +324,17 @@
</ul>
</t>
<t t-name="Menu.secondary.link">
- <a href="#"
+ <a t-attf-href="#menu_id=#{menu.id}&amp;action_id=#{menu.action ? menu.action.split(',')[1] : ''}"
t-att-class="menu.children.length ? 'oe_menu_toggler' : 'oe_menu_leaf'"
t-att-data-menu="menu.id"
t-att-data-action-model="menu.action ? menu.action.split(',')[0] : ''"
t-att-data-action-id="menu.action ? menu.action.split(',')[1] : ''">
<t t-esc="menu.name"/>
+ <t t-if="menu.needaction_enabled">
+ <div class="oe_menu_counter">
+ <t t-esc="menu.needaction_counter"/>
+ </div>
+ </t>
</a>
</t>
@@ -396,73 +363,96 @@
</a>
</li>
</t>
+<t t-name="About-Page">
+ <div>
+ <a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developper mode</a>
+ <h1 style="margin:0;">OpenERP</h1>
+ <h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3>
+ <p>
+ Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
+ OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.
+ </p>
+ <p>
+ Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a>
+ </p>
+ <p>
+ For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a>
+ </p>
+ </div>
+</t>
+<t t-name="Change_Pwd">
+ <form name="change_password_form" class="oe_forms" method="POST">
+ <table align="center">
+ <tr>
+ <td><label for="old_pwd">Old Password:</label></td>
+ <td><input type="password" name="old_pwd"
+ minlength="1" autofocus="autofocus"/></td>
+ </tr>
+ <tr>
+ <td><label for="new_password">New Password:</label></td>
+ <td><input type="password" name="new_password"
+ minlength="1" autofocus="autofocus"/></td>
+ </tr>
+ <tr>
+ <td><label for="confirm_pwd">Confirm Password:</label></td>
+ <td><input type="password" name="confirm_pwd"
+ minlength="1"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
+ </tr>
+ </table>
+ </form>
+</t>
<t t-name="ViewManager">
- <table class="view-manager-main-table" cellpadding="0" cellspacing="0">
- <tbody>
- <tr>
- <td class="view-manager-main-content">
- <div class="oe-view-manager-header">
- <h2 class="oe_view_title" t-if="self.flags.display_title !== false">
- <span class="oe_view_title_text"><t t-esc="self.display_title()"/></span>
- </h2>
- <div class="oe_vm_switch">
- <t t-if="views.length != 1" t-foreach="views" t-as="view">
-
- <button type="button" t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="view.label || view.view_type">
- <span><t t-esc="view.label || view.view_type"/></span>
- </button>
- </t>
- </div>
- </div>
- <div t-attf-id="#{prefix}_search" t-opentag="true"/>
- <t t-foreach="views" t-as="view">
- <div t-attf-id="#{prefix}_view_#{view.view_type}"/>
- </t>
- </td>
- <td class="view-manager-main-sidebar" height="100%">
- <t t-foreach="views" t-as="view">
- <div t-attf-id="#{prefix}_sidebar_#{view.view_type}" class="sidebar-main-div closed-sidebar" style="display: none"/>
+ <div class="oe_view_manager">
+ <table class="oe_view_manager_header2">
+ <col width="33%"/>
+ <col width="33%"/>
+ <col width="34%"/>
+ <tr>
+ <td colspan="2">
+ <h2 class="oe_view_title" t-if="widget.flags.display_title !== false">
+ <span class="oe_view_title_text"><t t-esc="widget.display_title()"/></span>
+ </h2>
+ </td>
+ <td> <div class="oe_view_manager_view_search" t-opentag="true"/> </td>
+ </tr>
+ <tr>
+ <td> <div class="oe_view_manager_buttons"/> </td>
+ <td> <div class="oe_view_manager_sidebar"/> </td>
+ <td>
+ <ul class="oe_view_manager_switch oe_button_group oe_right">
+ <t t-if="widget.views_src.length != 1" t-foreach="widget.views_src" t-as="view">
+ <li><a t-attf-class="oe_vm_switch_#{view.view_type} oe_i" t-att-data-view-type="view.view_type" t-att-title="view.label || view.view_type">
+ </a></li>
+ </t>
+ </ul>
+ <div class="oe_view_manager_pager oe_right"/>
+ </td>
+ </tr>
+ </table>
+ <div class="oe_view_manager_body">
+ <t t-foreach="widget.views_src" t-as="view">
+ <div t-attf-class="oe_view_manager_view_#{view.view_type}"/>
</t>
- </td>
- </tr>
- </tbody>
- </table>
+ </div>
+ </div>
</t>
-
-<t t-extend="ViewManager" t-name="ViewManagerAction">
- <t t-jquery=".view-manager-main-table tbody" t-operation="prepend">
- <tr>
- <td class="oe_view_manager_menu_tips" colspan="2">
- <blockquote t-if="self.action.help and !self.flags.low_profile
- and !(self.action.id in self.session.hidden_menutips)">
- <p><t t-esc="self.action.help"/></p>
- <div>
- <button type="button" name="hide">Hide this tip</button>
- <button type="button" name="disable">Disable all tips</button>
- </div>
- </blockquote>
- </td>
- </tr>
- </t>
+<t t-name="ViewManagerAction" t-extend="ViewManager">
<t t-jquery="h2.oe_view_title" t-operation="prepend">
- <a t-if="self.flags.display_title !== false" class="oe-shortcut-toggle" title="Add / Remove Shortcut..."
+ <a t-if="widget.flags.display_title !== false" class="oe-shortcut-toggle" title="Add / Remove Shortcut..."
href="javascript: void(0)"> </a>
</t>
<t t-jquery="h2.oe_view_title" t-operation="after">
- <select t-if="self.session.debug" class="oe_debug_view"/>
- </t>
- <t t-jquery=".oe-view-manager-header" t-operation="after">
- <div class="oe-view-manager-logs oe-folded">
- <ul></ul>
- <a class="oe-more-logs" href="#">More…</a>
- <a class="oe-remove-everything ui-icon ui-icon-closethick"/>
- </div>
+ <select t-if="widget.session.debug" class="oe_debug_view"/>
</t>
</t>
<t t-name="ViewManagerDebug">
<option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option>
- <option t-if="_.indexOf(['form', 'page'], view_manager.active_view) gt -1" value="perm_read" data-views="form,page">View Log (perm_read)</option>
+ <option t-if="view_manager.active_view === 'form'" value="perm_read" data-views="form,page">View Log (perm_read)</option>
+ <option t-if="view_manager.active_view === 'form'" value="toggle_layout_outline">Toggle Form Layout Outline</option>
<option value="fields">View Fields</option>
<option value="fvg">Fields View Get</option>
<t t-if="view_manager.session.uid === 1">
@@ -494,55 +484,84 @@
<span><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></span>
</div>
</t>
-<t t-extend="ViewManager" t-name="One2Many.viewmanager">
- <t t-jquery="span.oe_view_title_text" t-operation="replace"/>
-
- <t t-jquery=".oe-view-manager-header">
- this.attr('t-if', 'views.length != 1');
- </t>
+<t t-name="ViewPager">
+ <div class="oe_pager_value">
+ <t t-raw="__content__"/>
+ </div>
+ <ul class="oe_pager_group">
+ <!--
+ <button class="oe_button oe_button_pager" type="button" data-pager-action="first">
+ <img t-att-src='_s + "/web/static/src/img/pager_first.png"'/>
+ </button>
+ -->
+ <li>
+ <a class="oe_i" type="button" data-pager-action="previous">(</a>
+ </li>
+ <li>
+ <a class="oe_i" type="button" data-pager-action="next">)</a>
+ </li>
+ <!--
+ <button class="oe_button oe_button_pager" type="button" data-pager-action="last">
+ <img t-att-src='_s + "/web/static/src/img/pager_last.png"'/>
+ </button>
+ -->
+ </ul>
</t>
<t t-name="Sidebar">
- <a class="toggle-sidebar"></a>
- <div class="sidebar-content">
- <div class="sidebar-actions">
- </div>
+ <div class="oe_sidebar">
+ <t t-foreach="widget.sections" t-as="section">
+ <div class="oe_form_dropdown_section">
+ <button class="oe_dropdown_toggle"><t t-esc="section.label"/></button>
+ <ul class="oe_dropdown_menu">
+ <li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
+ <a class="oe_sidebar_action_a" t-att-title="item.title" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url" target="_blank">
+ <t t-raw="item.label"/>
+ </a>
+ </li>
+ <li t-if="section.name == 'files'" class="oe_sidebar_add" style="display:none;">
+ Input type file stuff
+ </li>
+ </ul>
+ </div>
+ </t>
</div>
</t>
-<t t-name="Sidebar.section">
- <div t-att-id="section_id" t-att-class="classname">
- <h2><t t-esc="name"/></h2>
+<t t-name="Sidebar.attachments">
+ <div class="oe-sidebar-attachments-toolbar">
+ <div class="oe-binary-file-set" style="float: right">
+ <form class="oe-binary-form" t-attf-target="#{element_id}_iframe"
+ method="post" enctype="multipart/form-data" action="/web/binary/upload_attachment">
+ <input type="hidden" name="session_id" t-att-value="session.session_id"/>
+ <input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/>
+ <input type="hidden" name="model" t-att-value="view.dataset.model"/>
+ <input type="hidden" name="id" t-att-value="view.datarecord.id"/>
+ <button class="oe_button" type="button">
+ <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/>
+ <span>Add</span>
+ </button>
+ <input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
+ t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/>
+ </form>
+ <iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"/>
+ </div>
</div>
-</t>
-<t t-name="Sidebar.section.items">
- <li t-foreach="items" t-as="item" t-att-class="item.classname">
- <a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#">
- <t t-esc="item.label"/>
- </a>
- </li>
+ <br style="clear: both"/>
+ <ul class="oe-sidebar-attachments-items">
+ <li t-foreach="attachments" t-as="attachment">
+ <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id='
+ + session.session_id + '&amp;model=ir.attachment&amp;id=' + attachment.id
+ + '&amp;field=datas&amp;filename_field=name&amp;t=' + (new Date().getTime())"/>
+ <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
+ <t t-esc="attachment.name"/>
+ </a>
+ <a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}">
+ <img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/>
+ </a>
+ </li>
+ </ul>
</t>
-<t t-name="TranslateDialog">
- <table t-if="widget.view.translatable_fields" class="oe_frame oe_forms oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%">
- <tr>
- <td class="oe_form_separator" width="1%" nowrap="nowrap">
- <div class="separator horizontal">Field</div>
- </td>
- <th t-foreach="widget.languages" align="left">
- <div class="separator horizontal"><t t-esc="name"/></div>
- </th>
- </tr>
- <tr t-foreach="widget.view.translatable_fields" t-as="field" t-att-data-field="field.name">
- <td class="oe_form_frame_cell" width="1%" nowrap="nowrap">
- <label class="oe_label"><t t-esc="field.string"/>:</label>
- </td>
- <td t-foreach="widget.languages" t-as="lg" class="oe_form_frame_cell">
- <input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" data-value="" class="oe_trad_field" style="width: 100%"/>
- <textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" data-value="" class="oe_trad_field" style="width: 100%"></textarea>
- </td>
- </tr>
- </table>
-</t>
<t t-name="TreeView">
<select t-if="toolbar" style="width: 30%">
</select>
@@ -585,44 +604,14 @@
</td>
</tr>
-<t t-name="ViewPager">
- <button class="oe_button oe_button_pager" type="button" data-pager-action="first">
- <img t-att-src='_s + "/web/static/src/img/pager_first.png"'/>
- </button>
- <button class="oe_button oe_button_pager" type="button" data-pager-action="previous">
- <img t-att-src='_s + "/web/static/src/img/pager_previous.png"'/>
- </button>
-
- <t t-raw="__content__"/>
-
- <button class="oe_button oe_button_pager" type="button" data-pager-action="next">
- <img t-att-src='_s + "/web/static/src/img/pager_next.png"'/>
- </button>
- <button class="oe_button oe_button_pager" type="button" data-pager-action="last">
- <img t-att-src='_s + "/web/static/src/img/pager_last.png"'/>
- </button>
-</t>
-
<table t-name="ListView" class="oe-listview-content">
<t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0) + (options.isClarkGable ? 1 : 0)"/>
- <thead class="ui-widget-header">
+ <thead>
<tr t-if="options.action_buttons !== false or options.pager !== false">
<th t-att-colspan="columns_count">
- <table>
- <tr>
- <td t-if="!no_leaf and options.action_buttons !== false" class="oe-actions">
- <button type="button" class="oe_button oe-list-add"
- t-if="options.addable">
- <t t-esc="options.addable"/>
- </button>
- <button type="button" class="oe_button oe-list-delete"
- t-if="options.selectable and options.deletable">
- Delete
- </button>
- </td>
- <t t-call="Listview.navigation.button"/>
- </tr>
- </table>
+ <div class="oe_list_buttons"/>
+ <div class="oe_list_sidebar"/>
+ <div class="oe_list_pager"/>
</th>
</tr>
<tr t-if="options.header" class="oe-listview-header-columns">
@@ -632,19 +621,19 @@
</th>
</t>
<th t-if="options.selectable" width="1" >
- <input type="checkbox" class="all-record-selector"/> </th>
- <th t-if="options.isClarkGable" width="1"> </th>
+ <input type="checkbox" class="all-record-selector"/>
+ </th>
+ <th t-if="options.isClarkGable" width="1"></th>
<t t-foreach="columns" t-as="column">
<th t-if="!column.meta and column.invisible !== '1'" t-att-data-id="column.id"
t-att-class="((options.sortable and column.tag !== 'button') ? 'oe-sortable' : null)">
- <t t-if="column.tag !== 'button'"
- ><t t-esc="column.string"/></t>
+ <t t-if="column.tag !== 'button'"><t t-esc="column.string"/></t>
</th>
</t>
<th t-if="options.deletable" width="1"/>
</tr>
</thead>
- <tfoot class="ui-widget-header">
+ <tfoot>
<tr>
<td t-if="options.selectable"/>
<td t-if="options.isClarkGable"/>
@@ -653,32 +642,26 @@
</td>
<td t-if="options.deletable"/>
</tr>
- <tr>
- <t t-call="Listview.navigation.button"/>
- </tr>
</tfoot>
</table>
-<t t-extend="ListView" t-name="One2Many.listview">
- <t t-jquery="thead.ui-widget-header > tr:first">
- this.removeAttr('t-if');
- </t>
-
- <t t-jquery="tfoot &gt; tr:last-child" t-operation="replace"/>
-
- <t t-jquery="td.oe-actions">
- this.removeAttr('t-if');
- </t>
- <t t-jquery="td.oe-actions" t-operation="prepend">
- <h3 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h3>
+<div t-name="ListView.buttons" class="oe_list_buttons">
+ <t t-if="!widget.no_leaf and widget.options.action_buttons !== false">
+ <button type="button" class="oe_button oe_list_add" t-if="widget.options.addable">
+ <t t-esc="widget.options.addable"/>
+ </button>
+ <button type="button" class="oe_button oe_list_delete" t-if="widget.options.selectable and widget.options.deletable">
+ Delete
+ </button>
</t>
+</div>
+<t t-name="ListView.pager">
+ <div class="oe_list_pager" t-att-colspan="widget.columns_count">
+ <t t-if="!widget.no_leaf and widget.options.pager !== false" t-call="ViewPager">
+ <span class="oe-pager-state">
+ </span>
+ </t>
+ </div>
</t>
-<th t-name="Listview.navigation.button" t-if="!no_leaf and options.pager !== false"
- class="oe-list-pager" t-att-colspan="columns_count">
- <t t-call="ViewPager">
- <span class="oe-pager-state">
- </span>
- </t>
-</th>
<t t-name="ListView.rows" t-foreach="records.length" t-as="index">
<t t-call="ListView.row">
<t t-set="record" t-value="records.at(index)"/>
@@ -711,73 +694,38 @@
</td>
</t>
<td t-if="options.deletable" class='oe-record-delete' width="1">
- <button type="button" name="delete"></button>
+ <button type="button" name="delete" class="oe_i">d</button>
</td>
</tr>
-<t t-name="ListView.row.form">
- <th t-if="widget.editable_list.options.selectable"></th>
- <th t-if="widget.editable_list.options.isClarkGable"></th>
-</t>
<t t-name="FormView">
- <div class="oe_form_header">
- <div class="oe_form_buttons" t-if="widget.options.action_buttons !== false">
- <button type="button" class="oe_button oe_form_button_save">Save</button>
- <button type="button" class="oe_button oe_form_button_cancel">Cancel</button>
- </div>
- <div class="oe_form_pager" t-if="widget.options.pager !== false">
- <t t-call="ViewPager">
- <span class="oe_pager_index">0</span><span class="oe_pager_separator"> / </span><span class="oe_pager_count">0</span>
- </t>
- </div>
+ <div class="oe_formview">
+ <div class="oe_form_buttons"/>
+ <div class="oe_form_sidebar"/>
+ <div class="oe_form_pager"/>
+ <div class="oe_form_container"/>
</div>
</t>
-<t t-name="One2Many.formview" t-extend="FormView">
- <t t-jquery=".oe_form_buttons" t-operation="inner">
- <button type="button" class="oe_button oe_form_button_create">Add</button>
- </t>
-</t>
-<t t-name="PageView" t-extend="FormView">
- <t t-jquery=".oe_form_buttons" t-operation="inner">
- <button type="button" class="oe_button oe_form_button_edit">Edit</button>
- <button type="button" class="oe_button oe_form_button_create">Create</button>
- <button type="button" class="oe_button oe_form_button_duplicate">Duplicate</button>
- <button type="button" class="oe_button oe_form_button_delete">Delete</button>
+<div t-name="FormView.buttons" class="oe_form_buttons">
+ <t t-if="widget.options.action_buttons !== false">
+ <span class="oe_form_buttons_view">
+ <button type="button" class="oe_button oe_form_button_edit">Edit</button>
+ <button type="button" class="oe_button oe_form_button_create">Create</button>
+ <button type="button" class="oe_button oe_form_button_duplicate">Duplicate</button>
+ <button type="button" class="oe_button oe_form_button_delete">Delete</button>
+ </span>
+ <span class="oe_form_buttons_edit">
+ <button type="button" class="oe_button oe_form_button_save">Save</button>
+ <button type="button" class="oe_button oe_form_button_cancel">Cancel</button>
+ </span>
</t>
-</t>
-<t t-name="FormView.sidebar.attachments">
- <div class="oe-sidebar-attachments-toolbar">
- <div class="oe-binary-file-set" style="float: right">
- <form class="oe-binary-form" t-attf-target="#{element_id}_iframe"
- method="post" enctype="multipart/form-data" action="/web/binary/upload_attachment">
- <input type="hidden" name="session_id" t-att-value="session.session_id"/>
- <input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/>
- <input type="hidden" name="model" t-att-value="view.dataset.model"/>
- <input type="hidden" name="id" t-att-value="view.datarecord.id"/>
- <button class="oe_button" type="button">
- <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/>
- <span>Add</span>
- </button>
- <input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
- t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/>
- </form>
- <iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"/>
- </div>
+</div>
+<t t-name="FormView.pager">
+ <div class="oe_form_pager">
+ <t t-if="widget.options.pager !== false" t-call="ViewPager">
+ <span class="oe_pager_index">0</span><span class="oe_pager_separator"> / </span><span class="oe_pager_count">0</span>
+ </t>
</div>
- <br style="clear: both"/>
- <ul class="oe-sidebar-attachments-items">
- <li t-foreach="attachments" t-as="attachment">
- <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id='
- + session.session_id + '&amp;model=ir.attachment&amp;id=' + attachment.id
- + '&amp;field=datas&amp;filename_field=name&amp;t=' + (new Date().getTime())"/>
- <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
- <t t-esc="attachment.name"/>
- </a>
- <a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}">
- <img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/>
- </a>
- </li>
- </ul>
</t>
<form t-name="FormView.set_default" class="oe_forms oe_frame">
<t t-set="args" t-value="widget.dialog_options.args"/>
@@ -789,7 +737,7 @@
Default:
</label>
</td>
- <td class="required">
+ <td class="oe_form_required">
<select id="formview_default_fields">
<option value=""/>
<option t-foreach="args.fields" t-as="field"
@@ -835,86 +783,69 @@
</tr>
</table>
</form>
-<t t-name="Widget">
- Unhandled widget
- <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t>
-</t>
-<t t-name="WidgetFrame">
- <table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
- <tr t-foreach="widget.table" t-as="row">
- <t t-foreach="row" t-as="td">
- <td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined"
- t-att-width="td.width"
- t-att-valign="td.table ? 'top' : undefined"
- t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}"
- >
- <t t-raw="td.render()"/>
- </td>
- </t>
- </tr>
- </table>
+
+<t t-name="FormRenderingForm.manual">
+ <div class="oe_form_manual_layout"/>
</t>
-<t t-name="WidgetFrame.readonly" t-extend="WidgetFrame">
- <t t-jquery="&gt;table">
- this.attr('class', this.attr('class')+' oe_form_readonly');
- </t>
+<t t-name="FormRenderingForm">
+ <div class="oe_form_sheetbg">
+ <div t-attf-class="oe_form_sheet oe_form_sheet_width #{classnames}">
+ <group col="4"/>
+ </div>
+ </div>
</t>
-<t t-name="WidgetGroup">
- <t t-if="widget.string">
- <fieldset class="oe_group_box">
- <legend><t t-esc="widget.string"/></legend>
- <t t-call="WidgetFrame"/>
- </fieldset>
+<t t-name="FormRenderingGroup">
+ <t t-set="table">
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" t-att-class="classnames"/>
</t>
- <t t-if="!widget.string">
- <t t-call="WidgetFrame"/>
+ <t t-if="string">
+ <fieldset class="oe_group_box">
+ <legend><t t-esc="string"/></legend>
+ <t t-raw="table"/>
+ </fieldset>
</t>
-</t>
-<t t-name="WidgetNotebook">
- <ul>
- <li t-foreach="widget.pages" t-as="page">
- <a href="#">
- <t t-esc="page.string"/>
- </a>
- </li>
- </ul>
- <t t-foreach="widget.pages" t-as="page">
- <div t-att-class="page.element_class">
- </div>
+ <t t-if="!string">
+ <t t-raw="table"/>
</t>
</t>
-<t t-name="WidgetNotebook.tooltip">
- <t t-foreach="widget.pages" t-as="page">
- <div class="oe_tooltip_string">
- Notebook Page "<t t-esc="page.string"/>"
- </div>
- <ul class="oe_tooltip_technical">
- <li data-item="modifiers">
- <span class="oe_tooltip_technical_title">Modifiers:</span>
- <t t-esc="page.node.attrs.modifiers"/>
+<t t-name="FormRenderingNotebook">
+ <div>
+ <ul t-attf-class="oe_form_notebook #{classnames}">
+ <li t-foreach="pages" t-as="page" t-att-modifiers="page.modifiers">
+ <a t-attf-href="##{page.id}">
+ <t t-esc="page.string"/>
+ </a>
</li>
</ul>
- </t>
+ </div>
</t>
-<t t-name="WidgetNotebookPage">
- <t t-call="WidgetFrame"/>
+<t t-name="FormRenderingNotebookPage">
+ <div t-attf-class="oe_form_notebook_page #{classnames}" t-att-id="id">
+ <group t-if="layout === 'auto'" col="4"/>
+ </div>
</t>
-<t t-name="WidgetSeparator">
- <div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
- <t t-esc="widget.string"/>
+<t t-name="FormRenderingSeparator">
+ <div t-attf-class="oe_horizontal_separator #{classnames}">
+ <t t-esc="string"/>
</div>
</t>
-<t t-name="WidgetLabel">
- <label t-att-for="widget.element_id"
- t-attf-class="oe_label#{widget.help ? '_help' : ''} oe_align_#{widget.align}">
- <t t-esc="widget.string"/>
- <span t-if="widget.help">?</span>
- <t t-if="widget.string and widget.node.tag != 'label'">:</t>
+<t t-name="FormRenderingLabel">
+ <label t-att-for="_for"
+ t-att-title="help"
+ t-attf-class="#{classnames} oe_form_label#{help ? '_help' : ''} oe_align_#{align}">
+ <t t-esc="string"/>
+ <span t-if="help">?</span>
+ :
</label>
</t>
+
+<t t-name="Widget">
+ Unhandled widget
+ <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t>
+</t>
<t t-name="WidgetLabel.tooltip">
<div class="oe_tooltip_string" t-if="widget.string">
- <t t-esc="widget.string"/> <t t-if="debug and widget.nolabel">(nolabel)</t>
+ <t t-esc="widget.node.attrs.string"/> <t t-if="debug and widget.nolabel">(nolabel)</t>
</div>
<p t-if="widget.help" class="oe_tooltip_help"><t t-esc="widget.help"/></p>
<ul t-if="debug" class="oe_tooltip_technical">
@@ -974,68 +905,88 @@
</li>
</ul>
</t>
-<t t-name="WidgetParagraph">
- <p t-attf-class="oe_form_paragraph oe_align_#{widget.align} #{widget.multilines ? 'oe_multilines' : ''}"><t t-esc="widget.string"/></p>
-</t>
<t t-name="FieldChar">
- <input t-att-type="widget.password ? 'password' : 'text'" size="1"
- t-att-name="widget.name"
- t-att-id="widget.element_id"
- t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}"
- style="width: 100%"
- /><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
-</t>
-<t t-name="FieldChar.readonly">
- <div
- t-att-id="widget.element_id"
- t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}">
- </div>
-</t>
-<t t-name="FieldURI.readonly">
- <div>
- <a href="#" class="oe_form_uri"/>
- </div>
+ <t t-if="!widget.get('effective_readonly')">
+ <div class="oe_form_field_char">
+ <input t-att-type="widget.password ? 'password' : 'text'"
+ t-att-id="widget.id_for_label"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ t-attf-class="field_char #{widget.is_field_number ? 'oe-number' : ''}"
+ /><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
+ </div>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div t-attf-class="oe_form_field_char #{widget.is_field_number ? 'oe-number' : ''}">
+ </div>
+ </t>
</t>
<t t-name="FieldEmail">
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
- <tr>
- <td width="100%">
- <t t-call="FieldChar"/>
- </td>
- <td width="16">
- <button class="oe_button oe_field_button" type="button" title="Send an e-mail with your default e-mail client">
- <img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/>
- </button>
- </td>
- </tr>
- </table>
+ <t t-if="!widget.get('effective_readonly')">
+ <table class="oe_form_field_email" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input type="text"
+ t-att-id="widget.id_for_label"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ />
+ </td>
+ <td width="16">
+ <button class="oe_button oe_field_button" tabindex="-1" type="button" title="Send an e-mail with your default e-mail client">
+ <img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div>
+ <a href="#" class="oe_form_uri"/>
+ </div>
+ </t>
</t>
<t t-name="FieldUrl">
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
- <tr>
- <td width="100%">
- <t t-call="FieldChar"/>
- </td>
- <td width="16">
- <button class="oe_button oe_field_button" type="button" title="Open this resource">
- <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/>
- </button>
- </td>
- </tr>
- </table>
+ <t t-if="!widget.get('effective_readonly')">
+ <table class="oe_form_field_url" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input type="text"
+ t-att-id="widget.id_for_label"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ />
+ </td>
+ <td width="16">
+ <button class="oe_button oe_field_button" tabindex="-1" type="button" title="Open this resource">
+ <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div>
+ <a href="#" class="oe_form_uri"/>
+ </div>
+ </t>
</t>
<t t-name="FieldText">
- <textarea rows="6"
- t-att-name="widget.name"
- t-att-id="widget.element_id"
- t-attf-class="field_#{widget.type}"
- style="width: 100%"
- ></textarea><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
+ <div class="oe_form_field_text">
+ <textarea rows="6"
+ t-att-name="widget.name"
+ class="field_text"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ ></textarea>
+ <img class="oe_field_translate oe_input_icon" t-if="widget.field.translate"
+ t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
+ </div>
</t>
<t t-name="web.datetimepicker">
- <div class="oe_datepicker_root">
- <input type="text" size="1" class="oe_datepicker_container" disabled="disabled" style="display: none;"/>
- <input type="text" size="1" style="width: 100%"
+ <div class="oe_datepicker_root oe_form_field_datetime">
+ <input type="text" class="oe_datepicker_container" disabled="disabled" style="display: none;"/>
+ <input type="text"
t-att-name="widget.name"
t-attf-class="oe_datepicker_master field_#{widget.type_of_date}"
/><img class="oe_input_icon oe_datepicker_trigger" t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
@@ -1043,86 +994,107 @@
</div>
</t>
<t t-name="FieldSelection">
- <select
- t-att-name="widget.name"
- t-att-id="widget.element_id"
- t-attf-class="field_#{widget.type}"
- style="width: 100%">
- <t t-foreach="widget.values" t-as="option">
- <option><t t-esc="option[1]"/></option>
- </t>
- </select>
+ <t t-if="!widget.get('effective_readonly')">
+ <div class="oe_form_field_selection">
+ <select
+ t-att-name="widget.name"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ t-att-id="widget.id_for_label">
+ <t t-foreach="widget.values" t-as="option">
+ <option><t t-esc="option[1]"/></option>
+ </t>
+ </select>
+ </div>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div class="field_selection">
+ </div>
+ </t>
</t>
<t t-name="FieldMany2One">
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
- <tr>
- <td width="100%" valign="top">
- <t t-call="FieldChar"/>
- <span class="oe-m2o-drop-down-button">
- <img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
- </td>
- <td>
- <button class="oe_button oe_field_button oe-m2o-cm-button" t-att-id="widget.name + '_open'">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY_MENU.png"'/>
- </button>
- <div t-att-id="widget.cm_id" class="contextMenu" style="display:none">
- </div>
- </td>
- </tr>
- </table>
-</t>
-<t t-name="FieldMany2One.context_menu">
- <ul>
- <li t-att-id="widget.cm_id + '_open'" style="color:grey" class="oe_m2o_menu_item_mandatory">Open...</li>
- <li t-att-id="widget.cm_id + '_create'" class="oe_m2o_menu_item_noreadonly">Create...</li>
- <li t-att-id="widget.cm_id + '_search'" class="oe_m2o_menu_item_noreadonly">Search...</li>
- <t t-set="i" t-value="0"/>
- <t t-foreach="widget.related_entries" t-as="entry">
- <li t-att-id="widget.cm_id + '_related_' + i" style="color:grey" class="oe_m2o_menu_item_mandatory">
- ... <t t-esc="(entry[2] || {})['name'] || ''"/>
- </li>
- <t t-set="i" t-value="i+1"/>
- </t>
- </ul>
-</t>
-<t t-name="FieldOne2Many">
-</t>
-<t t-name="FieldMany2Many">
- <div t-att-id="widget.list_id"></div>
+ <t t-if="!widget.get('effective_readonly')">
+ <table class="oe_form_field_many2one" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td valign="top">
+ <input type="text"
+ t-att-id="widget.id_for_label"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ />
+ <span class="oe-m2o-drop-down-button">
+ <img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
+ </td>
+ <td width="16">
+ <button class="oe_button oe_field_button oe-m2o-cm-button">
+ <img t-att-src='_s + "/web/static/src/img/icons/gtk-go-forward.png"'/>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div>
+ <a href="#" class="oe_form_uri"/>
+ </div>
+ </t>
</t>
<t t-name="FieldReference">
- <table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
- <tr>
- <td t-attf-class="oe_form_frame_cell oe_form_selection oe_form_view_reference_selection">
- </td>
- <td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none">
- </td>
- </tr>
- </table>
+ <t t-if="!widget.get('effective_readonly')">
+ <table class="oe_form_field_reference oe_frame oe_forms" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td t-attf-class="oe_form_frame_cell oe_form_selection oe_form_view_reference_selection">
+ </td>
+ <td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none">
+ </td>
+ </tr>
+ </table>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <a href="#" class="oe_form_uri"/>
+ </t>
</t>
<t t-name="FieldBoolean">
- <input type="checkbox"
- t-att-name="widget.name"
- t-att-id="widget.element_id"
- t-attf-class="field_#{widget.type}"/>
+ <span class="oe_form_field_boolean">
+ <input type="checkbox"
+ t-att-id="widget.id_for_label"
+ t-att-name="widget.name"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ class="field_boolean"/>
+ </span>
</t>
<t t-name="FieldProgressBar">
- <div t-opentag="true" class="oe-progressbar">
+ <div class="oe_form_field_progressbar" t-opentag="true">
<span></span>
</div>
</t>
+<t t-name="FieldStatus.content">
+ <ul class="oe-arrow-list">
+ <t t-set="size" t-value="widget.to_show.length"/>
+ <t t-foreach="_.range(size)" t-as="i">
+ <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe-arrow-list-selected' : ''">
+ <span class="oe-arrow-list-before" t-if="i &gt; 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i &lt; size - 1"></span>
+ </li>
+ </t>
+ </ul>
+</t>
+<t t-name="FieldStatus.content">
+ <ul class="oe_form_steps">
+ <t t-set="size" t-value="widget.to_show.length"/>
+ <t t-foreach="_.range(size)" t-as="i">
+ <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe_form_steps_active' : ''">
+ <t t-esc="widget.to_show[i][1]"/>
+ <img t-att-src='_s + "/web/static/src/img/form_steps.png"' class="oe_form_steps_arrow" t-if="i &lt; size - 1"/>
+ </li>
+ </t>
+ </ul>
+</t>
<t t-name="FieldBinaryImage">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
- <td align="center">
- <img t-att-src='_s + "/web/static/src/img/placeholder.png"' class="oe-binary-image"
- t-att-border="widget.readonly ? 0 : 1"
- t-att-id="widget.element_id + '_field'"
- t-att-name="widget.name"
- t-attf-class="field_#{widget.type}"
- t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
- t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
- />
+ <td class="oe_form_field-binary-image-placeholder" align="center">
+
</td>
</tr>
<tr>
@@ -1131,7 +1103,7 @@
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td>
- <div class="oe-binary-file-set" style="width: 40px; height:22px;">
+ <div class="oe-binary-file-set">
<form class="oe-binary-form" t-att-target="widget.iframe"
method="post" enctype="multipart/form-data" action="/web/binary/upload">
<input type="hidden" name="session_id" value=""/>
@@ -1139,7 +1111,10 @@
<button class="oe_button" type="button" title="Set Image">
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
</button>
- <input type="file" class="oe-binary-file" name="ufile"/>
+ <input type="file" class="oe-binary-file" name="ufile"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ />
</form>
</div>
</td>
@@ -1160,58 +1135,77 @@
</tr>
</table>
</t>
+<t t-name="FieldBinaryImage-img">
+ <img t-att-src='url' class="oe-binary-image field_image"
+ t-att-border="widget.readonly ? 0 : 1"
+ t-att-name="widget.name"
+ t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
+ t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
+ t-att-style="'max-width: ' + (widget.$element.width() > 60 ? widget.$element.width() : 60) + 'px'"
+ />
+</t>
<t t-name="FieldBinaryFile">
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
- <tr>
- <td width="100%">
- <input type="text" size="1" readonly="readonly"
- t-att-name="widget.name"
- t-att-id="widget.element_id + '_field'"
- t-attf-class="field_#{widget.type}" style="width: 100%"
- />
- </td>
- <td class="oe-binary" nowrap="true">
- <table cellspacing="0" cellpadding="0" border="0">
- <tr>
- <td>
- <div class="oe-binary-file-set" style="width: 80px; height:22px;">
- <form class="oe-binary-form" t-att-target="widget.iframe"
- method="post" enctype="multipart/form-data" action="/web/binary/upload">
- <input type="hidden" name="session_id" value=""/>
- <input type="hidden" name="callback" t-att-value="widget.iframe"/>
- <button class="oe_button oe_field_button" type="button" title="Set Image">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
- <span>Select</span>
- </button>
- <input type="file" class="oe-binary-file" name="ufile"/>
- </form>
- </div>
- </td>
- <td>
- <button class="oe_button oe-binary-file-save" type="button" title="Save As">
- <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/>
- <span>Save As</span>
- </button>
- </td>
- <td>
- <button class="oe_button oe-binary-file-clear" type="button" title="Clear">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
- <span>Clear</span>
- </button>
- </td>
- </tr>
- </table>
- </td>
- <td class="oe-binary-progress" style="display: none" nowrap="true">
- <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
- <b>Uploading ...</b>
- <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/>
- </td>
- </tr>
- </table>
+ <t t-if="!widget.get('effective_readonly')">
+ <table class="oe_form_field_binary" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input type="text" size="1" readonly="readonly"
+ t-att-name="widget.name"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ class="field_binary"
+ />
+ </td>
+ <td class="oe-binary" nowrap="true">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <div class="oe-binary-file-set" style="width: 80px; height:22px;">
+ <form class="oe-binary-form" t-att-target="widget.iframe"
+ method="post" enctype="multipart/form-data" action="/web/binary/upload">
+ <input type="hidden" name="session_id" value=""/>
+ <input type="hidden" name="callback" t-att-value="widget.iframe"/>
+ <button class="oe_button oe_field_button" type="button" title="Set Image">
+ <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
+ <span>Select</span>
+ </button>
+ <input type="file" class="oe-binary-file" name="ufile"/>
+ </form>
+ </div>
+ </td>
+ <td>
+ <button class="oe_button oe-binary-file-save" type="button" title="Save As">
+ <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/>
+ <span>Save As</span>
+ </button>
+ </td>
+ <td>
+ <button class="oe_button oe-binary-file-clear" type="button" title="Clear">
+ <img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
+ <span>Clear</span>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td class="oe-binary-progress" style="display: none" nowrap="true">
+ <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
+ <b>Uploading ...</b>
+ <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/>
+ </td>
+ </tr>
+ </table>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div>
+ <a href="javascript:void(0)" class="oe_form_uri"/>
+ </div>
+ </t>
</t>
<t t-name="WidgetButton">
- <button type="button" class="oe_button">
+ <button type="button" class="oe_button oe_form_button"
+ t-att-tabindex="widget.node.attrs.tabindex"
+ t-att-autofocus="widget.node.attrs.autofocus">
<img t-if="widget.node.attrs.icon" t-att-src="_s + '/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</button>
@@ -1248,17 +1242,57 @@
</t>
</t>
-<t t-name="SearchView">
- <form class="oe_forms">
- <t t-call="SearchView.render_lines"/>
- <div class="oe_search-view-buttons">
- <button class="oe_button">Search</button>
- <button class="oe_button" type="reset">Clear</button>
- <select class="oe_search-view-filters-management">
- </select>
- </div>
- </form>
+<t t-name="TranslateDialog">
+ <table t-if="widget.view.translatable_fields" class="oe_form oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td class="oe_form_separator" width="1%" nowrap="nowrap">
+ <div class="oe_horizontal_separator">Field</div>
+ </td>
+ <th t-foreach="widget.languages" align="left">
+ <div class="oe_horizontal_separator"><t t-esc="name"/></div>
+ </th>
+ </tr>
+ <tr t-foreach="widget.view.translatable_fields" t-as="field" t-att-data-field="field.name">
+ <td class="oe_form_group_cell" width="1%" nowrap="nowrap">
+ <label class="oe_label"><t t-esc="field.node.attrs.string"/>:</label>
+ </td>
+ <td t-foreach="widget.languages" t-as="lg" class="oe_form_group_cell">
+ <input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" data-value="" class="oe_trad_field" style="width: 100%"/>
+ <textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" data-value="" class="oe_trad_field" style="width: 100%"></textarea>
+ </td>
+ </tr>
+ </table>
+</t>
+
+<t t-name="One2Many.viewmanager" t-extend="ViewManager">
+ <t t-jquery="span.oe_view_title_text" t-operation="replace"/>
+
+ <t t-jquery=".oe-view-manager-header">
+ this.attr('t-if', 'views.length != 1');
+ </t>
+</t>
+<t t-name="One2Many.formview" t-extend="FormView">
+ <t t-jquery=".oe_form_buttons" t-operation="inner">
+ <button type="button" class="oe_button oe_form_button_create">Add</button>
+ </t>
+</t>
+<t t-name="One2Many.listview" t-extend="ListView">
+ <t t-jquery="thead.ui-widget-header > tr:first">
+ this.removeAttr('t-if');
+ </t>
+
+ <t t-jquery="tfoot &gt; tr:last-child" t-operation="replace"/>
+
+ <t t-jquery="td.oe-actions">
+ this.removeAttr('t-if');
+ </t>
+ <t t-jquery="td.oe-actions" t-operation="prepend">
+ <h3 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h3>
+ </t>
</t>
+
+<div t-name="SearchView" class="oe_searchview">
+</div>
<t t-name="SearchView.managed-filters">
<option class="oe-filters-title" value="">Filters</option>
<optgroup label="-- Filters --">
@@ -1314,10 +1348,28 @@
<br t-if="attrs.string"/>
<t t-esc="attrs.string"/>
</button>
-<span t-name="SearchView.filters" class="filter_label_group"
- ><t t-foreach="filters" t-as="filter"
- ><t t-raw="filter.render(defaults)"/></t
-></span>
+<ul t-name="SearchView.filters">
+ <li t-foreach="widget.filters" t-as="filter">
+ <t t-esc="filter.attrs.string or filter.attrs.name or 'Ω'"/>
+ </li>
+</ul>
+<t t-name="SearchView.filters.facet">
+ <div class="category oe_filter_category"><t t-esc="facet.get('category')"/></div>
+
+ <t t-set="val" t-value="facet.get('json')"/>
+
+ <div t-if="!(val instanceof Array)" class="search_facet_input_container">
+ <t t-esc="facet.get('value')"/>
+ </div>
+ <t t-if="val instanceof Array">
+ <div class="search_facet_input_container"
+ t-foreach="facet.get('json')" t-as="filter">
+ <t t-esc="filter.attrs.string || filter.attrs.name"/>
+ </div>
+ </t>
+
+ <div class="search_facet_remove VS-icon VS-icon-cancel"/>
+</t>
<t t-name="SearchView.field">
<label t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
t-att-title="attrs.help"
@@ -1375,8 +1427,7 @@
</div>
</t>
<t t-name="SearchView.util.expand">
- <div t-att-class="'searchview_group ' + (expand == '0' ? 'folded' : 'expanded')"
- t-att-id="element_id">
+ <div t-att-class="'searchview_group ' + (expand == '0' ? 'folded' : 'expanded')">
<a t-if="label" class="searchview_group_string" href="#">
<t t-esc="label"/>
</a>
@@ -1394,34 +1445,18 @@
</t>
</t>
</t>
-<t t-name="SearchView.extended_search">
- <t t-call="SearchView.util.expand">
- <t t-set="expand" t-value="false"/>
- <t t-set="label">Advanced Filters</t>
- <t t-set="content">
- <div class="searchview_extended_groups_list">
- </div>
- </t>
- </t>
-</t>
-<t t-name="SearchView.extended_search.group">
- <div t-att-id="widget.element_id" class="searchview_extended_group">
- <select class="searchview_extended_group_choice">
- <option value="any">Any of the following conditions must match</option>
- <option value="all">All the following conditions must match</option>
- <option value="none">None of the following conditions must match</option>
- </select>
- <a class="searchview_extended_delete_group"
- href="javascript:void(0)"><span></span></a>
- <div class="searchview_extended_propositions_list">
- </div>
- <a class="searchview_extended_add_proposition" href="javascript:void(0)">
- <span>Add condition</span></a>
- <div class="oe_adv_filters_and"><span>and</span></div>
- </div>
-</t>
+<div t-name="SearchView.advanced" class="oe_searchview_advanced">
+ <h4>Advanced Search...</h4>
+ <form>
+ <ul>
+
+ </ul>
+ <button class="oe_add_condition" type="button">Add a condition</button>
+ <button class="oe_apply" type="submit">Apply</button>
+ </form>
+</div>
<t t-name="SearchView.extended_search.proposition">
- <div t-att-id="widget.element_id">
+ <li>
<select class="searchview_extended_prop_field">
<t t-foreach="widget.attrs.fields" t-as="field">
<option t-if="typeof field.store === 'undefined' || field.store || field.fnct_search"
@@ -1435,7 +1470,7 @@
<span class="searchview_extended_prop_value"/>
<a class="searchview_extended_delete_prop"
href="javascript:void(0)"><span> </span></a>
- </div>
+ </li>
</t>
<t t-name="SearchView.extended_search.proposition.char">
<input t-att-id="widget.element_id" class="field_char"/>
@@ -1459,20 +1494,20 @@
</select>
</t>
<t t-name="SelectCreatePopup">
- <div t-att-id="widget.element_id">
+ <div>
<table style="width:100%">
<tr style="width:100%">
<td style="width:100%">
- <div t-att-id="widget.element_id + '_search'" style="width:100%"></div>
+ <div class="oe-select-create-popup-view-search" style="width:100%"></div>
</td>
</tr>
<tr style="width:100%">
<td style="width:100%">
- <div t-att-id="widget.element_id + '_view_list'" style="width:100%"></div>
+ <div class="oe-select-create-popup-view-list" style="width:100%"></div>
</td>
</tr>
</table>
- <div t-att-id="widget.element_id + '_view_form'" style="width:100%"></div>
+ <div class="oe-select-create-popup-view-form" style="width:100%"></div>
</div>
</t>
<t t-name="SelectCreatePopup.search.buttons">
@@ -1490,8 +1525,8 @@
<button type="button" class="oe_button oe_selectcreatepopup-form-close">Cancel</button>
</t>
<t t-name="FormOpenPopup">
- <div t-att-id="widget.element_id">
- <div t-att-id="widget.element_id + '_view_form'" style="width:100%"></div>
+ <div>
+ <div class="oe-form-open-popup-form-view" style="width:100%"></div>
</div>
</t>
<t t-name="FormOpenPopup.form.buttons">
@@ -1507,21 +1542,7 @@
<td t-if="edited and !options.deletable" class="oe-listview-padding"/>
</t>
</t>
-<t t-name="ListView.row.frame">
- <t t-foreach="widget.table" t-as="row">
- <t t-foreach="row" t-as="td">
- <td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined"
- t-att-valign="td.table ? 'top' : undefined"
- t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class} oe-field-cell"
- >
- <t t-raw="td.render()"/>
- </td>
- </t>
- </t>
- <td>
- <button class="oe-edit-row-save" type="button"> </button>
- </td>
-</t>
+
<t t-name="view_editor">
<table class="oe_view_editor">
<t t-call="view_editor.row"/>
@@ -1585,6 +1606,7 @@
<t t-name="vieweditor_boolean">
<input type="checkbox" t-att-id="widget.name"/>
</t>
+
<t t-name="ExportView">
<a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a>
</t>
@@ -1636,7 +1658,6 @@
</td>
</tr>
</table>
-
<table t-name="ExportTreeView-Secondary"
id="field-tree-structure" class="oe_export_fields_selector_export"
cellspacing="0" cellpadding="0">
@@ -1670,7 +1691,6 @@
</table>
</td>
</tr>
-
<t t-name="ExportNewList">
<tr>
<th><label>Save as:</label></th>
@@ -1678,7 +1698,6 @@
<td><button class="oe_button oe_export_button_export" id="add_export_list">Ok</button></td>
</tr>
</t>
-
<t t-name="Exists.ExportList">
<label for="saved_export_list">Saved exports:</label>
@@ -1690,30 +1709,6 @@
</select>
<button class="oe_button oe_export_button_export" id="delete_export_list" type="button">Delete</button>
</t>
-<t t-name="Change_Pwd">
- <form name="change_password_form" class="oe_forms" method="POST">
- <table align="center">
- <tr>
- <td><label for="old_pwd">Old Password:</label></td>
- <td><input type="password" name="old_pwd"
- minlength="1" autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="new_password">New Password:</label></td>
- <td><input type="password" name="new_password"
- minlength="1" autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="confirm_pwd">Confirm Password:</label></td>
- <td><input type="password" name="confirm_pwd"
- minlength="1"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
- </tr>
- </table>
- </form>
-</t>
<t t-name="ImportView">
<a id="importview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Import</a>
@@ -1790,34 +1785,4 @@
</t>
</t>
-<t t-name="About-Page">
- <div>
- <a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developper mode</a>
- <h1 style="margin:0;">OpenERP</h1>
- <h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3>
- <p>
- Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
- OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.
- </p>
- <p>
- Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a>
- </p>
- <p>
- For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a>
- </p>
- </div>
-</t>
-<t t-name="FieldStatus.content">
- <ul class="oe-arrow-list">
- <t t-set="size" t-value="widget.to_show.length"/>
- <t t-foreach="_.range(size)" t-as="i">
- <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe-arrow-list-selected' : ''">
- <span class="oe-arrow-list-before" t-if="i &gt; 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i &lt; size - 1"></span>
- </li>
- </t>
- </ul>
-</t>
-<t t-name="EmptyComponent">
- <div></div>
-</t>
</templates>