[merge] mobile web client

bzr revid: nicolas.vanhoren@openerp.com-20110715101403-6n58t47l46p0vaka
This commit is contained in:
niv-openerp
2011-07-15 12:14:03 +02:00
8 changed files with 568 additions and 269 deletions
+7 -2
View File
@@ -107,11 +107,13 @@
_session_id: "session" + session_counter++,
screen: openerp.screen,
sessions: openerp.sessions,
base: {}
base: {},
web_mobile: {}
};
openerp.sessions[new_instance._session_id] = new_instance;
if (!skip_init)
if (!skip_init){
openerp.base(new_instance);
}
return new_instance;
}
};
@@ -147,6 +149,9 @@ openerp.base = function(instance) {
if (openerp.base.list && openerp.base.list.editable) {
openerp.base.list.editable(instance);
}
if (openerp.web_mobile) {
openerp.web_mobile(instance);
}
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
@@ -0,0 +1,4 @@
.ui-collapsible-content-expanded {
display: block;
}
@@ -0,0 +1,217 @@
openerp.web_mobile.chrome_mobile = function(openerp) {
openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
this.rpc('/base/session/sc_list',{} ,function(res){
self.$element.html(QWeb.render("Shortcuts", {'sc' : res}))
self.$element.find("a").click(self.on_clicked);
})
},
on_clicked: function(ev) {
$shortcut = $(ev.currentTarget);
id = $shortcut.data('menu');
res_id = $shortcut.data('res');
jQuery("#oe_header").find("h1").html($shortcut.data('name'));
this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", res_id);
this.listview.start();
}
});
openerp.web_mobile.Header = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
this.$element.html(QWeb.render("Header", this));
this.$element.find("a").click(this.on_clicked);
},
on_clicked: function(ev) {
$opt = $(ev.currentTarget);
current_id = $opt.attr('id');
if (current_id == 'home') {
this.homepage = new openerp.web_mobile.Login(this.session, "oe_app");
this.homepage.on_login_valid();
}
}
});
openerp.web_mobile.Secondary = openerp.base.Controller.extend({
init: function(session, element_id, secondary_menu_id) {
this._super(session, element_id);
this.data = secondary_menu_id;
},
start: function(ev, id) {
var v = { menu : this.data };
this.$element.html(QWeb.render("Menu.secondary", v));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
},
on_menu_click: function(ev, id) {
$menu = $(ev.currentTarget);
id = $menu.data('menu');
for (var i = 0; i < this.data.children.length; i++) {
if (this.data.children[i].id == id) {
this.children = this.data.children[i];
}
}
jQuery("#oe_header").find("h1").html($menu.data('name'));
var child_len = this.children.children.length;
if (child_len > 0) {
this.$element
.removeClass("secondary_menu")
.addClass("content_menu");
//.hide();
this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children);
this.secondary.start();
}
else {
if (id) {
this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", id);
this.listview.start();
}
}
}
});
openerp.web_mobile.Menu = openerp.base.Controller.extend({
init: function(session, element_id, secondary_menu_id) {
this._super(session, element_id);
this.secondary_menu_id = secondary_menu_id;
this.$secondary_menu = $("#" + secondary_menu_id);
this.menu = false;
},
start: function() {
this.rpc("/base/menu/load", {}, this.on_loaded);
},
on_loaded: function(data) {
this.data = data;
this.$element.html(QWeb.render("Menu", this.data));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
},
on_menu_click: function(ev, id) {
$menu = $(ev.currentTarget);
id = $menu.data('menu');
for (var i = 0; i < this.data.data.children.length; i++) {
if (this.data.data.children[i].id == id) {
this.children = this.data.data.children[i];
}
}
jQuery("#oe_header").find("h1").html($menu.data('name'));
this.$element
.removeClass("login_valid")
.addClass("secondary_menu");
//.hide();
this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children);
this.secondary.start();
}
});
openerp.web_mobile.Options = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
this.$element.html(QWeb.render("Options", this));
self.$element.find("#logout").click(self.on_logout);
},
on_logout: function(ev) {
this.session.logout();
this.login = new openerp.web_mobile.Login(this.session, "oe_app");
this.login.start();
}
});
openerp.web_mobile.Login = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
jQuery("#oe_header").children().remove();
this.rpc("/base/session/get_databases_list", {}, function(result) {
self.db_list = result.db_list;
self.$element.html(QWeb.render("Login", self));
self.$element.find('#database').click(self.on_db_select);
self.$element.find("#login").click(self.on_login);
$.mobile.initializePage();
})
},
on_db_select: function(ev) {
var db = this.$element.find("#database option:selected").val();
jQuery("#db_text").html(db);
},
on_login: function(ev) {
ev.preventDefault();
var self = this;
var $e = this.$element;
var db = $e.find("div select[name=database]").val();
var login = $e.find("div input[name=login]").val();
var password = $e.find("div input[name=password]").val();
//$e.hide();
// Should hide then call callback
this.session.session_login(db, login, password, function() {
if(self.session.session_is_valid()) {
self.on_login_valid();
} else {
self.on_login_invalid();
}
});
},
on_login_invalid: function() {
this.$element
.removeClass("login_valid")
.addClass("login_invalid")
.show();
},
on_login_valid: function() {
this.$element
.removeClass("login_invalid")
.addClass("login_valid");
//.hide();
this.$element.html(QWeb.render("HomePage", {}));
this.header = new openerp.web_mobile.Header(this.session, "oe_header");
this.shortcuts = new openerp.web_mobile.Shortcuts(this.session, "oe_shortcuts");
this.menu = new openerp.web_mobile.Menu(this.session, "oe_menu", "oe_secondary_menu");
this.options = new openerp.web_mobile.Options(this.session, "oe_options");
this.header.start();
this.shortcuts.start();
this.menu.start();
this.options.start();
jQuery("#oe_header").find("h1").html('Home');
},
do_ask_login: function(continuation) {
this.on_login_invalid();
this.on_login_valid.add({
position: "last",
unique: true,
callback: continuation
});
}
});
openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({
init: function(element_id) {
var self = this;
this._super(null, element_id);
QWeb.add_template("xml/web_mobile.xml");
var params = {};
this.$element.html(QWeb.render("WebClient", {}));
this.session = new openerp.base.Session("oe_errors");
this.crashmanager = new openerp.base.CrashManager(this.session);
this.login = new openerp.web_mobile.Login(this.session, "oe_app");
this.session.on_session_invalid.add(this.login.do_ask_login);
},
start: function() {
this.session.start();
this.login.start();
}
});
openerp.web_mobile.mobilewebclient = function(element_id) {
// TODO Helper to start mobile webclient rename it openerp.base.webclient
var client = new openerp.web_mobile.MobileWebClient(element_id);
client.start();
return client;
};
}
@@ -0,0 +1,104 @@
openerp.web_mobile.form_mobile = function (openerp) {
openerp.web_mobile.FormView = openerp.base.Controller.extend({
init: function(session, element_id, list_id, action) {
this._super(session, element_id);
this.list_id = list_id;
this.action = action;
},
start: function() {
var self = this;
id = this.list_id;
model = this.action.res_model;
view_id = this.action.views[1][0];
this.dataset = new openerp.base.DataSetSearch(this.session, this.action.res_model, null, null);
this.dataset.read_slice(false, false, false, function (result) {
for (var i = 0; i < result.length; i++) {
if (result[i].id == id) {
var data = result[i];
}
}
self.rpc("/base/formview/load", {"model": model, "view_id": view_id }, function (result) {
var view_fields = result.fields_view.arch.children;
var get_fields = self.get_fields(view_fields);
for (var j = 0; j < view_fields.length; j++) {
if (view_fields[j].tag == 'notebook') {
var notebooks = view_fields[j];
}
}
$("#oe_header").find("h1").html(result.fields_view.arch.attrs.string);
self.$element.html(QWeb.render("FormView", {'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : result.fields_view.fields, 'values' : data }));
self.$element.find('a').click(function(){
for (var k = 0; k < notebooks.children.length; k++) {
var text = $(this).find('.ui-btn-text').text();
var next = $(this).next();
var span = $(this).find('span .ui-icon');
var content_expanded_class = "ui-collapsible-content ui-collapsible-content-expanded";
var content_collapsed_class = "ui-collapsible-content ui-collapsible-content-collapsed";
var expanded_class = "ui-collapsible-content-expanded";
var collapsed_class = "ui-collapsible-content-collapsed";
var plus_class = "ui-icon-plus";
var minus_class = "ui-icon-minus";
if (notebooks.children[k].attrs.string == $.trim(text)) {
get_fields = self.get_fields(notebooks.children[k].children);
$(this).parents().find('form').find('div').each( function() {
var prev = $(this).prev();
var trim_class = $.trim($(this).attr('class'));
if (trim_class == content_expanded_class || trim_class == content_collapsed_class) {
prev.removeClass('ui-btn-active');
if ($.trim(prev.find('.ui-btn-text').text()) != notebooks.children[k].attrs.string) {
prev.find('span .ui-icon').removeClass(minus_class);
prev.find('span .ui-icon').addClass(plus_class);
$(this).removeClass(expanded_class);
$(this).addClass(collapsed_class);
}
}
});
if (next.hasClass(content_collapsed_class)) {
span.removeClass(plus_class);
span.addClass(minus_class);
next.removeClass(collapsed_class);
next.addClass(expanded_class);
}
else if (next.hasClass(content_expanded_class)) {
span.removeClass(minus_class);
span.addClass(plus_class);
next.removeClass(expanded_class);
next.addClass(collapsed_class);
}
if (!next.find('.detail').html().length) {
next.find('.detail').append(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields_view.fields, 'values' : data }));
}
}
}
});
});
});
},
get_fields: function(view_fields, fields) {
this.fields = fields || [];
for (var i=0; i < view_fields.length; i++){
if (view_fields[i].tag == 'field') {
this.fields.push(view_fields[i]);
}
if (view_fields[i].tag == 'group') {
this.get_fields(view_fields[i].children, this.fields);
}
if (view_fields[i].tag == 'separator') {
this.get_fields(view_fields[i].children, this.fields);
}
}
return this.fields;
},
});
}
@@ -0,0 +1,67 @@
openerp.web_mobile.list_mobile = function (openerp) {
openerp.web_mobile.ListView = openerp.base.Controller.extend({
init: function(session, element_id, list_id) {
this._super(session, element_id);
this.list_id = list_id;
},
start: function() {
this.rpc('/base/menu/action', {'menu_id': this.list_id},
this.on_menu_action_loaded);
},
on_menu_action_loaded: function(data) {
var self = this;
if (data.action.length) {
this.action = data.action[0][2];
this.on_search_data('');
}
},
on_search_data: function(request){
if(request){
if(request.term){
var search_val = request.term;
}else{
if(request.which==27 || request.which==13 || request.which==9){
var search_val = '';
}else if(request.which==38 || request.which==40 || request.which==39 || request.which==37){
return;
}else if($("#searchid").val()==""){
var search_val = '';
}else{
return;
}
}
}
else{
var search_val = '';
}
var self = this;
var dataset = new openerp.base.DataSetStatic(this.session, this.action.res_model, this.action.context);
dataset.domain=[['name','ilike',search_val]];
dataset.name_search(search_val, dataset.domain, 'ilike',false ,function(result){
self.$element.html(QWeb.render("ListView", {'records' : result.result}));
self.$element.find("#searchid").focus();
if(request.term){
self.$element.find("#searchid").val(request.term);
}
self.$element.find("#searchid").autocomplete({
source: function(req) { self.on_search_data(req); },
focus: function(e, ui) {
e.preventDefault();
},
html: true,
minLength: 0,
delay: 0
});
self.$element.find("#searchid").keyup(self.on_search_data);
self.$element.find("a#list-id").click(self.on_list_click);
});
},
on_list_click: function(ev) {
$record = $(ev.currentTarget);
var self = this;
id = $record.data('id');
this.formview = new openerp.web_mobile.FormView(this.session, "oe_app", id, this.action);
this.formview.start();
}
});
}
+4 -256
View File
@@ -1,257 +1,5 @@
openerp.web_mobile = function(openerp) {
openerp.web_mobile = {};
openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({
init: function(element_id) {
var self = this;
this._super(null, element_id);
QWeb.add_template("xml/web_mobile.xml");
var params = {};
this.$element.html(QWeb.render("WebClient", {}));
this.session = new openerp.base.Session("oe_errors");
this.crashmanager = new openerp.base.CrashManager(this.session);
this.login = new openerp.web_mobile.Login(this.session, "oe_app");
this.session.on_session_invalid.add(this.login.do_ask_login);
},
start: function() {
this.session.start();
this.login.start();
}
});
openerp.web_mobile.mobilewebclient = function(element_id) {
// TODO Helper to start mobile webclient rename it openerp.base.webclient
var client = new openerp.web_mobile.MobileWebClient(element_id);
client.start();
return client;
};
openerp.web_mobile.Header = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
this.$element.html(QWeb.render("Header", this));
this.$element.find("a").click(this.on_clicked);
},
on_clicked: function(ev) {
$opt = $(ev.currentTarget);
current_id = $opt.attr('id');
if (current_id == 'home') {
this.homepage = new openerp.web_mobile.Login(this.session, "oe_app");
this.homepage.on_login_valid();
}
}
});
openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
this.rpc('/base/session/sc_list',{} ,function(res){
self.$element.html(QWeb.render("Shortcuts", {'sc' : res}))
self.$element.find("a").click(self.on_clicked);
})
},
on_clicked: function(ev) {
$shortcut = $(ev.currentTarget);
id = $shortcut.data('menu');
res_id = $shortcut.data('res');
jQuery("#oe_header").find("h1").html($shortcut.data('name'));
this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", res_id);
this.listview.start();
}
});
openerp.web_mobile.ListView = openerp.base.Controller.extend({
init: function(session, element_id, list_id) {
this._super(session, element_id);
this.list_id = list_id;
},
start: function() {
this.rpc('/base/menu/action', {'menu_id': this.list_id},
this.on_menu_action_loaded);
},
on_menu_action_loaded: function(data) {
var self = this;
if (data.action.length) {
var action = data.action[0][2];
self.on_action(action);
}
},
on_action: function(action) {
var self = this;
var view_id = action.views[0][0];
(new openerp.base.DataSetSearch(this.session, action.res_model, null, null))
.read_slice(false, false, false, function(result){
this.listview = new openerp.web_mobile.ListView(this.session, "oe_app");
self.$element.html(QWeb.render("ListView", {'records' : result}));
});
}
});
openerp.web_mobile.Secondary = openerp.base.Controller.extend({
init: function(session, element_id, secondary_menu_id) {
this._super(session, element_id);
this.data = secondary_menu_id;
},
start: function(ev, id) {
var v = { menu : this.data };
this.$element.html(QWeb.render("Menu.secondary", v));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
},
on_menu_click: function(ev, id) {
$menu = $(ev.currentTarget);
id = $menu.data('menu');
for (var i = 0; i < this.data.children.length; i++) {
if (this.data.children[i].id == id) {
this.children = this.data.children[i];
}
}
jQuery("#oe_header").find("h1").html($menu.data('name'));
var child_len = this.children.children.length;
if (child_len > 0) {
this.$element
.removeClass("secondary_menu")
.addClass("content_menu");
//.hide();
this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children);
this.secondary.start();
}
else {
if (id) {
this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", id);
this.listview.start();
}
}
}
});
openerp.web_mobile.Menu = openerp.base.Controller.extend({
init: function(session, element_id, secondary_menu_id) {
this._super(session, element_id);
this.secondary_menu_id = secondary_menu_id;
this.$secondary_menu = $("#" + secondary_menu_id);
this.menu = false;
},
start: function() {
this.rpc("/base/menu/load", {}, this.on_loaded);
},
on_loaded: function(data) {
this.data = data;
this.$element.html(QWeb.render("Menu", this.data));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
},
on_menu_click: function(ev, id) {
$menu = $(ev.currentTarget);
id = $menu.data('menu');
for (var i = 0; i < this.data.data.children.length; i++) {
if (this.data.data.children[i].id == id) {
this.children = this.data.data.children[i];
}
}
jQuery("#oe_header").find("h1").html($menu.data('name'));
this.$element
.removeClass("login_valid")
.addClass("secondary_menu");
//.hide();
this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children);
this.secondary.start();
}
});
openerp.web_mobile.Options = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
this.$element.html(QWeb.render("Options", this));
self.$element.find("#logout").click(self.on_logout);
},
on_logout: function(ev) {
this.session.logout();
this.login = new openerp.web_mobile.Login(this.session, "oe_app");
this.login.start();
}
});
openerp.web_mobile.Login = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
},
start: function() {
var self = this;
jQuery("#oe_header").children().remove();
this.rpc("/base/session/get_databases_list", {}, function(result) {
self.db_list = result.db_list;
self.$element.html(QWeb.render("Login", self));
self.$element.find('#database').click(self.on_db_select);
self.$element.find("#login").click(self.on_login);
$.mobile.initializePage();
})
},
on_db_select: function(ev) {
var db = this.$element.find("#database option:selected").val();
jQuery("#db_text").html(db);
},
on_login: function(ev) {
ev.preventDefault();
var self = this;
var $e = this.$element;
var db = $e.find("div select[name=database]").val();
var login = $e.find("div input[name=login]").val();
var password = $e.find("div input[name=password]").val();
//$e.hide();
// Should hide then call callback
this.session.session_login(db, login, password, function() {
if(self.session.session_is_valid()) {
self.on_login_valid();
} else {
self.on_login_invalid();
}
});
},
on_login_invalid: function() {
this.$element
.removeClass("login_valid")
.addClass("login_invalid")
.show();
},
on_login_valid: function() {
this.$element
.removeClass("login_invalid")
.addClass("login_valid");
//.hide();
this.$element.html(QWeb.render("HomePage", {}));
this.header = new openerp.web_mobile.Header(this.session, "oe_header");
this.shortcuts = new openerp.web_mobile.Shortcuts(this.session, "oe_shortcuts");
this.menu = new openerp.web_mobile.Menu(this.session, "oe_menu", "oe_secondary_menu");
this.options = new openerp.web_mobile.Options(this.session, "oe_options");
this.header.start();
this.shortcuts.start();
this.menu.start();
this.options.start();
jQuery("#oe_header").find("h1").html('Home');
},
do_ask_login: function(continuation) {
this.on_login_invalid();
this.on_login_valid.add({
position: "last",
unique: true,
callback: continuation
});
}
});
openerp.web_mobile = function(instance) {
openerp.web_mobile.chrome_mobile(instance);
openerp.web_mobile.list_mobile(instance);
openerp.web_mobile.form_mobile(instance);
};
+11 -7
View File
@@ -2,11 +2,13 @@
<html style="height: 100%">
<head>
<meta charset="utf-8" />
<title>Login Page OpenERP mobile</title>
<title>OpenERP Web Mobile</title>
<link rel="stylesheet" href="/web_mobile/static/lib/jquery_mobile/css/jquery.mobile-1.0a4.1.css" />
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery/jquery-1.5.2.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script>
<script type="text/javascript" src="/web_mobile/static/lib/jquery_mobile/js/jquery.mobile-1.0a4.1.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
<script type="text/javascript" src="/base/static/lib/underscore/underscore.js"></script>
@@ -17,21 +19,23 @@
<script type="text/javascript" src="/base/static/src/js/base.js"></script>
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
<script type="text/javascript" src="/base/static/src/js/data.js"></script>
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/chrome_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/list_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/form_mobile.js"></script>
<link rel="stylesheet" href="/web_mobile/static/src/css/web_mobile.css"/>
<script type="text/javascript">
$(function() {
QWeb = window.QWeb || new QWeb2.Engine();
var oe = openerp.init();
openerp.web_mobile(oe);
oe.web_mobile.mobilewebclient("moe");
$("html").page();
});
</script>
</head>
</head>
<body>
<div id="moe" class="openerp" data-role="page"></div>
+154 -4
View File
@@ -36,7 +36,7 @@
<span id="db_text" class="ui-btn-text">Choose Database...</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select data-native-menu="false" id="database" name="database" tabindex="-1">
<select id="database" name="database">
<t t-foreach="db_list" t-as="opt">
<option t-att-value="opt"><t t-esc="opt"/></option>
</t>
@@ -67,12 +67,17 @@
<t t-name="ListView">
<div role="main" class="ui-content" data-role="content">
<ul data-role="listview" class="ui-listview">
<form class="ui-listview-filter ui-bar-c" role="search">
<div id="search-data" class="ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield ui-body-c">
<input id="searchid" placeholder="Filter items..." data-type="search" class="ui-input-text ui-body-null" autosearch="true" setTimeDefault="700"/>
</div>
</form>
<ul role="listbox" data-role="listview" class="ui-listview">
<li data-theme="c" class="ui-btn ui-btn-icon-right ui-li ui-btn-up-c" t-foreach="records" t-as="record">
<div class="ui-btn-inner ui-li">
<div class="ui-btn-text">
<a href="#" class="ui-link-inherit">
<span><t t-esc="record.name"/></span>
<a id="list-id" t-att-data-id="record[0]" href="#" class="ui-link-inherit">
<span><t t-esc="record[1]"/></span>
</a>
</div>
<span class="ui-icon ui-icon-arrow-r"></span>
@@ -82,6 +87,151 @@
</div>
</t>
<t t-name="FormView">
<div role="main" class="ui-content" data-role="content">
<form>
<t t-foreach="get_fields" t-as="field">
<div data-role="fieldcontain">
<label class="ui-input-text">
<span>
<t t-if="!(field.attrs.string)" t-esc="fields[field.attrs.name].string"></t>
<t t-if="field.attrs.string" t-esc="field.attrs.string"></t>
</span>:
</label>
<t t-if="fields[field.attrs.name].type == 'char'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'boolean'">
<t t-if="values[field.attrs.name]">
<input type="checkbox" checked="yes"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input type="checkbox"/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'text'">
<input type="text" style="width: 100%"/><br/>
</t>
<t t-if="fields[field.attrs.name].type == 'selection'">
<div class="ui-select">
<div data-theme="c" class="ui-btn ui-btn-icon-right ui-btn-corner-all ui-shadow ui-btn-up-c">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">
<t t-if="values[field.attrs.name]">
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<t t-if="opt[0] == (values[field.attrs.name])">
<t t-esc="opt[1]"/>
</t>
</t>
</t>
</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select>
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<option t-att-value="opt[0]"><t t-esc="opt[1]"/></option>
</t>
</select>
</div>
</div>
</t>
<t t-if="fields[field.attrs.name].type == 'many2one'">
<div class="ui-select">
<div data-theme="c" class="ui-btn ui-btn-icon-right ui-btn-corner-all ui-shadow ui-btn-up-c">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">
<t t-esc="values[field.attrs.name][1]"/>
</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select>
<t t-if="fields[field.attrs.name].selection">
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<option t-att-value="opt[0]"><t t-esc="opt[1]"/></option>
</t>
</t>
</select>
</div>
</div>
</t>
<t t-if="fields[field.attrs.name].type == 'one2many'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'many2many'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'float'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'date'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'datetime'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
</div>
</t>
<t t-if="notebooks">
<t t-foreach="notebooks.children" t-as="page">
<div class="info ui-collapsible-contain" data-collapsed="true" data-role="collapsible">
<h1 class="ui-collapsible-heading">
<a class="ui-collapsible-heading-toggle ui-btn ui-btn-icon-left ui-shadow ui-corner-all ui-btn-up-c" href="#" data-theme="c" t-id="page.attrs.string">
<span class="ui-btn-inner ui-corner-all">
<span class="ui-btn-text">
<t t-esc="page.attrs.string"></t>
<!-- <span class="ui-collapsible-heading-status"></span> -->
</span>
<span data-theme="d" class="ui-btn ui-btn-icon-left ui-btn-corner-all ui-shadow ui-btn-up-d">
<span class="ui-btn-inner ui-btn-corner-all ui-corner-all">
<span class="ui-btn-text"></span>
<span class="ui-icon ui-icon-shadow ui-icon-plus"></span>
</span>
</span>
</span>
</a>
<div class="ui-collapsible-content ui-collapsible-content-collapsed" aria-hidden="true">
<div class="detail"></div>
</div>
</h1>
</div>
</t>
</t>
</form>
</div>
</t>
<t t-name="Shortcuts">
<ul data-dividertheme="b" data-theme="c" data-inset="true" data-role="listview" class="ui-listview ui-listview-inset ui-corner-all ui-shadow">
<li data-role="list-divider" role="heading" class="ui-li ui-li-divider ui-btn ui-bar-b ui-corner-top ui-btn-up-undefined">