[MERGE] Merge from openerp-web.
bzr revid: jra@tinyerp.com-20111003051633-9z4qdvet7rbz9z7p
This commit is contained in:
@@ -9,4 +9,3 @@ RE:^include/
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
logging.cfg
|
||||
|
||||
@@ -16,6 +16,7 @@ def wsgi_postload():
|
||||
_logger.info("embedded mode")
|
||||
o = Options()
|
||||
o.dbfilter = openerp.tools.config['dbfilter']
|
||||
o.server_wide_modules = openerp.conf.server_wide_modules or ['web']
|
||||
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions")
|
||||
o.addons_path = openerp.modules.module.ad_paths
|
||||
o.serve_static = True
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"depends" : [],
|
||||
'active': True,
|
||||
'post_load' : 'wsgi_postload',
|
||||
'web_preload' : True,
|
||||
'js' : [
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
"static/lib/datejs/core.js",
|
||||
@@ -27,6 +26,7 @@
|
||||
"static/lib/underscore/underscore.js",
|
||||
"static/lib/underscore/underscore.string.js",
|
||||
"static/lib/labjs/LAB.src.js",
|
||||
"static/lib/py.parse/lib/py.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/core.js",
|
||||
"static/src/js/dates.js",
|
||||
|
||||
@@ -13,7 +13,6 @@ import xmlrpclib
|
||||
import simplejson
|
||||
import werkzeug.datastructures
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.urls
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
@@ -98,7 +97,8 @@ class WebRequest(object):
|
||||
self.params = dict(params)
|
||||
# OpenERP session setup
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession(self.config))
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
|
||||
self.session.config = self.config
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
@@ -309,9 +309,16 @@ class Root(object):
|
||||
by the server, will be filtered by this pattern
|
||||
"""
|
||||
def __init__(self, options):
|
||||
self.root = werkzeug.urls.Href('/web/webclient/home')
|
||||
self.root = '/web/webclient/home?debug=1'
|
||||
self.config = options
|
||||
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connector(protocol='local')
|
||||
else:
|
||||
conn = openerplib.get_connector(hostname=self.config.server_host,
|
||||
port=self.config.server_port)
|
||||
self.config.connector = conn
|
||||
|
||||
self.session_cookie = 'sessionid'
|
||||
self.addons = {}
|
||||
|
||||
@@ -341,13 +348,10 @@ class Root(object):
|
||||
request.parameter_storage_class = werkzeug.datastructures.ImmutableDict
|
||||
|
||||
if request.path == '/':
|
||||
return werkzeug.utils.redirect(
|
||||
self.root(dict(request.args, debug='')), 301)(
|
||||
environ, start_response)
|
||||
return werkzeug.utils.redirect(self.root, 301)(environ, start_response)
|
||||
elif request.path == '/mobile':
|
||||
return werkzeug.utils.redirect(
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(
|
||||
environ, start_response)
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)
|
||||
|
||||
handler = self.find_handler(*(request.path.split('/')[1:]))
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ class NonLiteralEncoder(simplejson.encoder.JSONEncoder):
|
||||
'__eval_context': object.get_eval_context()
|
||||
}
|
||||
raise TypeError('Could not encode unknown non-literal %s' % object)
|
||||
|
||||
_ALLOWED_KEYS = frozenset(['__ref', "__id", '__domains',
|
||||
'__contexts', '__eval_context', 'own_values'])
|
||||
|
||||
def non_literal_decoder(dct):
|
||||
""" Decodes JSON dicts into :class:`Domain` and :class:`Context` based on
|
||||
@@ -50,6 +53,9 @@ def non_literal_decoder(dct):
|
||||
``own_values`` dict key.
|
||||
"""
|
||||
if '__ref' in dct:
|
||||
for x in dct.keys():
|
||||
if not x in _ALLOWED_KEYS:
|
||||
raise ValueError("'%s' key not allowed in non literal domain/context" % x)
|
||||
if dct['__ref'] == 'domain':
|
||||
domain = Domain(None, key=dct['__id'])
|
||||
if 'own_values' in dct:
|
||||
@@ -231,7 +237,10 @@ class CompoundContext(BaseContext):
|
||||
|
||||
def evaluate(self, context=None):
|
||||
ctx = dict(context or {})
|
||||
ctx.update(self.get_eval_context() or {})
|
||||
eval_context = self.get_eval_context()
|
||||
if eval_context:
|
||||
eval_context = self.session.eval_context(eval_context)
|
||||
ctx.update(eval_context)
|
||||
final_context = {}
|
||||
for context_to_eval in self.contexts:
|
||||
if not isinstance(context_to_eval, (dict, BaseContext)):
|
||||
|
||||
@@ -38,6 +38,7 @@ Code repository: https://code.launchpad.net/~niv-openerp/openerp-client-lib/trun
|
||||
import xmlrpclib
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
@@ -70,6 +71,14 @@ class Connector(object):
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
|
||||
def get_service(self, service_name):
|
||||
"""
|
||||
Returns a Service instance to allow easy manipulation of one of the services offered by the remote server.
|
||||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return Service(self, service_name)
|
||||
|
||||
class XmlRPCConnector(Connector):
|
||||
"""
|
||||
A type of connector that uses the XMLRPC protocol.
|
||||
@@ -208,10 +217,7 @@ class LocalConnector(Connector):
|
||||
# OpenERPWarning code 1
|
||||
# OpenERPException code 2
|
||||
try:
|
||||
result = openerp.netsvc.dispatch_rpc(service_name, method, args, None)
|
||||
except openerp.netsvc.OpenERPDispatcherException, e:
|
||||
fault = xmlrpclib.Fault(openerp.tools.exception_to_unicode(e.exception), e.traceback)
|
||||
raise fault
|
||||
result = openerp.netsvc.dispatch_rpc(service_name, method, args)
|
||||
except:
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
fault = xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value))
|
||||
@@ -323,7 +329,7 @@ class Connection(object):
|
||||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return Service(self.connector, service_name)
|
||||
return self.connector.get_service(service_name)
|
||||
|
||||
class AuthenticationError(Exception):
|
||||
"""
|
||||
@@ -370,7 +376,7 @@ class Model(object):
|
||||
index = {}
|
||||
for r in result:
|
||||
index[r['id']] = r
|
||||
result = [index[x] for x in args[0]]
|
||||
result = [index[x] for x in args[0] if x in index]
|
||||
self.__logger.debug('result: %r', result)
|
||||
return result
|
||||
return proxy
|
||||
|
||||
@@ -5,6 +5,9 @@ import time
|
||||
import openerplib
|
||||
|
||||
import nonliterals
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
#----------------------------------------------------------
|
||||
# OpenERPSession RPC openerp backend access
|
||||
#----------------------------------------------------------
|
||||
@@ -26,27 +29,24 @@ class OpenERPSession(object):
|
||||
Used to store references to non-literal domains which need to be
|
||||
round-tripped to the client browser.
|
||||
"""
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
def __init__(self):
|
||||
self.config = None
|
||||
self._db = False
|
||||
self._uid = False
|
||||
self._login = False
|
||||
self._password = False
|
||||
self._locale = 'en_US'
|
||||
self.context = {}
|
||||
self.contexts_store = {}
|
||||
self.domains_store = {}
|
||||
self._lang = {}
|
||||
self.remote_timezone = 'utc'
|
||||
self.client_timezone = False
|
||||
|
||||
def __getstate__(self):
|
||||
state = dict(self.__dict__)
|
||||
if "config" in state:
|
||||
del state['config']
|
||||
return state
|
||||
|
||||
def build_connection(self):
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connection(protocol='local', database=self._db,
|
||||
login=self._login, user_id=self._uid, password=self._password)
|
||||
else:
|
||||
conn = openerplib.get_connection(hostname=self.config.server_host,
|
||||
port=self.config.server_port, database=self._db, login=self._login,
|
||||
conn = openerplib.Connection(self.config.connector, database=self._db, login=self._login,
|
||||
user_id=self._uid, password=self._password)
|
||||
return conn
|
||||
|
||||
@@ -103,15 +103,6 @@ class OpenERPSession(object):
|
||||
self.context = self.model('res.users').context_get(self.context)
|
||||
self.context = self.context or {}
|
||||
|
||||
self.client_timezone = self.context.get("tz", False)
|
||||
# invalid code, anyway we decided the server will be in UTC
|
||||
#if self.client_timezone:
|
||||
# self.remote_timezone = self.execute('common', 'timezone_get')
|
||||
|
||||
self._locale = self.context.get('lang','en_US')
|
||||
lang_ids = self.execute('res.lang','search', [('code', '=', self._locale)])
|
||||
if lang_ids:
|
||||
self._lang = self.execute('res.lang', 'read',lang_ids[0], [])
|
||||
return self.context
|
||||
|
||||
@property
|
||||
|
||||
@@ -68,29 +68,6 @@ class Xml2Json:
|
||||
# OpenERP Web web Controllers
|
||||
#----------------------------------------------------------
|
||||
|
||||
def manifest_preload():
|
||||
modules = [k for k,v in openerpweb.addons_manifest.items() if v.get('web_preload')]
|
||||
return modules
|
||||
|
||||
def manifest_addons(addons):
|
||||
if addons==None:
|
||||
addons = manifest_preload()
|
||||
else:
|
||||
addons = addons.split(',')
|
||||
return addons
|
||||
|
||||
def manifest_glob(addons, key):
|
||||
addons = manifest_addons(addons)
|
||||
files = []
|
||||
for addon in addons:
|
||||
manifest = openerpweb.addons_manifest.get(addon, {})
|
||||
addons_path = manifest['addons_path']
|
||||
globlist = manifest.get(key, [])
|
||||
for pattern in globlist:
|
||||
for path in glob.glob(os.path.join(addons_path, addon, pattern)):
|
||||
files.append(path[len(addons_path):])
|
||||
return files
|
||||
|
||||
# TODO change into concat_file(addons,key) taking care of addons_path
|
||||
def concat_files(addons_path, file_list):
|
||||
""" Concatenate file content
|
||||
@@ -131,24 +108,45 @@ home_template = textwrap.dedent("""<!DOCTYPE html>
|
||||
class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/web/webclient"
|
||||
|
||||
def server_wide_modules(self, req):
|
||||
addons = [i for i in req.config.server_wide_modules if i in openerpweb.addons_manifest]
|
||||
return addons
|
||||
|
||||
def manifest_glob(self, req, addons, key):
|
||||
if addons==None:
|
||||
addons = self.server_wide_modules(req)
|
||||
else:
|
||||
addons = addons.split(',')
|
||||
files = []
|
||||
for addon in addons:
|
||||
manifest = openerpweb.addons_manifest.get(addon, None)
|
||||
if not manifest:
|
||||
continue
|
||||
addons_path = manifest['addons_path']
|
||||
globlist = manifest.get(key, [])
|
||||
for pattern in globlist:
|
||||
for path in glob.glob(os.path.join(addons_path, addon, pattern)):
|
||||
files.append(path[len(addons_path):])
|
||||
return files
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def csslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'css')
|
||||
return self.manifest_glob(req, mods, 'css')
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def jslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'js')
|
||||
return self.manifest_glob(req, mods, 'js')
|
||||
|
||||
@openerpweb.httprequest
|
||||
def css(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'css')
|
||||
files = self.manifest_glob(req, mods, 'css')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'text/css')])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def js(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'js')
|
||||
files = self.manifest_glob(req, mods, 'js')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'application/javascript')])
|
||||
@@ -158,19 +156,19 @@ class WebClient(openerpweb.Controller):
|
||||
# script tags
|
||||
jslist = ['/web/webclient/js']
|
||||
if req.debug:
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'js')]
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, None, 'js')]
|
||||
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
|
||||
|
||||
# css tags
|
||||
csslist = ['/web/webclient/css']
|
||||
if req.debug:
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'css')]
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, None, 'css')]
|
||||
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
|
||||
|
||||
r = home_template % {
|
||||
'javascript': js,
|
||||
'css': css,
|
||||
'modules': simplejson.dumps(manifest_preload()),
|
||||
'modules': simplejson.dumps(self.server_wide_modules(req)),
|
||||
}
|
||||
return r
|
||||
|
||||
@@ -364,7 +362,7 @@ class Session(openerpweb.Controller):
|
||||
mods = []
|
||||
for name, manifest in openerpweb.addons_manifest.items():
|
||||
# TODO replace by ir.module.module installed web
|
||||
if not manifest.get('web_preload') and manifest.get('active', True):
|
||||
if name not in req.config.server_wide_modules and manifest.get('active', True):
|
||||
mods.append(name)
|
||||
return mods
|
||||
|
||||
@@ -494,6 +492,9 @@ def clean_action(req, action):
|
||||
if isinstance(action.get('domain'), basestring):
|
||||
action['domain'] = eval( action['domain'], eval_ctx ) or []
|
||||
|
||||
if 'type' not in action:
|
||||
action['type'] = 'ir.actions.act_window_close'
|
||||
|
||||
if action['type'] == 'ir.actions.act_window':
|
||||
return fix_view_modes(action)
|
||||
return action
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: 87fb1b67d6a13f10a1a328104ee4d4b2c36801ec
|
||||
branch: default
|
||||
latesttag: 0.2
|
||||
latesttagdistance: 1
|
||||
@@ -0,0 +1 @@
|
||||
Parser and evaluator of Python expressions
|
||||
@@ -0,0 +1,14 @@
|
||||
* Parser
|
||||
since parsing expressions, try with a pratt parser
|
||||
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
http://effbot.org/zone/simple-top-down-parsing.htm
|
||||
|
||||
Evaluator
|
||||
---------
|
||||
|
||||
* Stop busyworking trivial binary operator
|
||||
* Make it *trivial* to build Python type-wrappers
|
||||
* Implement Python's `data model
|
||||
protocols<http://docs.python.org/reference/datamodel.html#basic-customization>`_
|
||||
for *all* supported operations, optimizations can come later
|
||||
* Automatically type-wrap everything (for now anyway)
|
||||
@@ -0,0 +1,546 @@
|
||||
var py = {};
|
||||
(function (exports) {
|
||||
var NUMBER = /^\d$/,
|
||||
NAME_FIRST = /^[a-zA-Z_]$/,
|
||||
NAME = /^[a-zA-Z0-9_]$/;
|
||||
|
||||
var create = function (o, props) {
|
||||
function F() {}
|
||||
F.prototype = o;
|
||||
var inst = new F;
|
||||
for(var name in props) {
|
||||
if(!props.hasOwnProperty(name)) { continue; }
|
||||
inst[name] = props[name];
|
||||
}
|
||||
return inst;
|
||||
};
|
||||
|
||||
var symbols = {};
|
||||
var comparators = {};
|
||||
var Base = {
|
||||
nud: function () { throw new Error(this.id + " undefined as prefix"); },
|
||||
led: function (led) { throw new Error(this.id + " undefined as infix"); },
|
||||
toString: function () {
|
||||
if (this.id === '(constant)' || this.id === '(number)' || this.id === '(name)' || this.id === '(string)') {
|
||||
return [this.id.slice(0, this.id.length-1), ' ', this.value, ')'].join('');
|
||||
} else if (this.id === '(end)') {
|
||||
return '(end)';
|
||||
} else if (this.id === '(comparator)' ) {
|
||||
var repr = ['(comparator', this.expressions[0]];
|
||||
for (var i=0;i<this.operators.length; ++i) {
|
||||
repr.push(this.operators[i], this.expressions[i+1]);
|
||||
}
|
||||
return repr.join(' ') + ')';
|
||||
}
|
||||
var out = [this.id, this.first, this.second, this.third]
|
||||
.filter(function (r){return r}).join(' ');
|
||||
return '(' + out + ')';
|
||||
}
|
||||
};
|
||||
function symbol(id, bp) {
|
||||
bp = bp || 0;
|
||||
var s = symbols[id];
|
||||
if (s) {
|
||||
if (bp > s.lbp) {
|
||||
s.lbp = bp;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return symbols[id] = create(Base, {
|
||||
id: id,
|
||||
lbp: bp
|
||||
});
|
||||
}
|
||||
function constant(id) {
|
||||
symbol(id).nud = function () {
|
||||
this.id = "(constant)";
|
||||
this.value = id;
|
||||
return this;
|
||||
};
|
||||
}
|
||||
function prefix(id, bp, nud) {
|
||||
symbol(id).nud = nud || function () {
|
||||
this.first = expression(bp);
|
||||
return this
|
||||
}
|
||||
}
|
||||
function infix(id, bp, led) {
|
||||
symbol(id, bp).led = led || function (left) {
|
||||
this.first = left;
|
||||
this.second = expression(bp);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
function infixr(id, bp) {
|
||||
symbol(id, bp).led = function (left) {
|
||||
this.first = left;
|
||||
this.second = expression(bp - 1);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
function comparator(id) {
|
||||
comparators[id] = true;
|
||||
var bp = 60;
|
||||
infix(id, bp, function (left) {
|
||||
this.id = '(comparator)';
|
||||
this.operators = [id];
|
||||
this.expressions = [left, expression(bp)];
|
||||
while (token.id in comparators) {
|
||||
this.operators.push(token.id);
|
||||
advance();
|
||||
this.expressions.push(
|
||||
expression(bp));
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
constant('None'); constant('False'); constant('True');
|
||||
|
||||
symbol('(number)').nud = function () { return this; };
|
||||
symbol('(name)').nud = function () { return this; };
|
||||
symbol('(string)').nud = function () { return this; };
|
||||
symbol('(end)');
|
||||
|
||||
symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(',');
|
||||
symbol('else');
|
||||
|
||||
symbol('lambda', 20).nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== ':') {
|
||||
for(;;) {
|
||||
if (token.id !== '(name)') {
|
||||
throw new Error('Excepted an argument name');
|
||||
}
|
||||
this.first.push(token);
|
||||
advance();
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(':');
|
||||
this.second = expression();
|
||||
return this;
|
||||
};
|
||||
infix('if', 20, function (left) {
|
||||
this.first = left;
|
||||
this.second = expression();
|
||||
advance('else');
|
||||
this.third = expression();
|
||||
return this;
|
||||
});
|
||||
|
||||
infixr('or', 30); infixr('and', 40); prefix('not', 50);
|
||||
|
||||
comparator('in'); comparator('not in');
|
||||
comparator('is'); comparator('is not');
|
||||
comparator('<'); comparator('<=');
|
||||
comparator('>'); comparator('>=');
|
||||
comparator('<>'); comparator('!='); comparator('==');
|
||||
|
||||
infix('|', 70); infix('^', 80), infix('&', 90);
|
||||
|
||||
infix('<<', 100); infix('>>', 100);
|
||||
|
||||
infix('+', 110); infix('-', 110);
|
||||
|
||||
infix('*', 120); infix('/', 120);
|
||||
infix('//', 120), infix('%', 120);
|
||||
|
||||
prefix('-', 130); prefix('+', 130); prefix('~', 130);
|
||||
|
||||
infixr('**', 140);
|
||||
|
||||
infix('.', 150, function (left) {
|
||||
if (token.id !== '(name)') {
|
||||
throw new Error('Expected attribute name, got ', token.id);
|
||||
}
|
||||
this.first = left;
|
||||
this.second = token;
|
||||
advance();
|
||||
return this;
|
||||
});
|
||||
symbol('(', 150).nud = function () {
|
||||
this.first = [];
|
||||
var comma = false;
|
||||
if (token.id !== ')') {
|
||||
while (true) {
|
||||
if (token.id === ')') {
|
||||
break;
|
||||
}
|
||||
this.first.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
comma = true;
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(')');
|
||||
if (!this.first.length || comma) {
|
||||
return this;
|
||||
} else {
|
||||
return this.first[0];
|
||||
}
|
||||
};
|
||||
symbol('(').led = function (left) {
|
||||
this.first = left;
|
||||
this.second = [];
|
||||
if (token.id !== ")") {
|
||||
for(;;) {
|
||||
this.second.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(")");
|
||||
return this;
|
||||
|
||||
};
|
||||
infix('[', 150, function (left) {
|
||||
this.first = left;
|
||||
this.second = expression();
|
||||
advance("]");
|
||||
return this;
|
||||
});
|
||||
symbol('[').nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== ']') {
|
||||
for (;;) {
|
||||
if (token.id === ']') {
|
||||
break;
|
||||
}
|
||||
this.first.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(']');
|
||||
return this;
|
||||
};
|
||||
|
||||
symbol('{').nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== '}') {
|
||||
for(;;) {
|
||||
if (token.id === '}') {
|
||||
break;
|
||||
}
|
||||
var key = expression();
|
||||
advance(':');
|
||||
var value = expression();
|
||||
this.first.push([key, value]);
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance('}');
|
||||
return this;
|
||||
};
|
||||
|
||||
var longops = {
|
||||
'*': ['*'],
|
||||
'<': ['<', '=', '>'],
|
||||
'>': ['=', '>'],
|
||||
'!': ['='],
|
||||
'=': ['='],
|
||||
'/': ['/']
|
||||
};
|
||||
function Tokenizer() {
|
||||
this.states = ['initial'];
|
||||
this.tokens = [];
|
||||
}
|
||||
Tokenizer.prototype = {
|
||||
builder: function (empty) {
|
||||
var key = this.states[0] + '_builder';
|
||||
if (empty) {
|
||||
var value = this[key];
|
||||
delete this[key];
|
||||
return value;
|
||||
} else {
|
||||
return this[key] = this[key] || [];
|
||||
}
|
||||
},
|
||||
simple: function (type) {
|
||||
this.tokens.push({type: type});
|
||||
},
|
||||
push: function (new_state) {
|
||||
this.states.push(new_state);
|
||||
},
|
||||
pop: function () {
|
||||
this.states.pop();
|
||||
},
|
||||
|
||||
feed: function (str, index) {
|
||||
var s = this.states;
|
||||
return this[s[s.length - 1]](str, index);
|
||||
},
|
||||
|
||||
initial: function (str, index) {
|
||||
var character = str[index];
|
||||
|
||||
if (character in longops) {
|
||||
var follow = longops[character];
|
||||
for(var i=0, len=follow.length; i<len; ++i) {
|
||||
if (str[index+1] === follow[i]) {
|
||||
character += follow[i];
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character === ' ') {
|
||||
return index+1;
|
||||
} else if (character === '\0') {
|
||||
this.tokens.push(symbols['(end)']);
|
||||
return index + 1
|
||||
} else if (character === '"' || character === "'") {
|
||||
this.push('string');
|
||||
return index + 1;
|
||||
} else if (NUMBER.test(character)) {
|
||||
this.push('number');
|
||||
return index;
|
||||
} else if (NAME_FIRST.test(character)) {
|
||||
this.push('name');
|
||||
return index;
|
||||
} else if (character in symbols) {
|
||||
this.tokens.push(create(symbols[character]));
|
||||
return index + 1;
|
||||
}
|
||||
throw new Error("Tokenizing failure of <<" + str + ">> at index " + index
|
||||
+ ", character [[" + character + "]]"
|
||||
+ "; parsed so far: " + this.tokens);
|
||||
},
|
||||
string: function (str, index) {
|
||||
var character = str[index];
|
||||
if (character === '"' || character === "'") {
|
||||
this.tokens.push(create(symbols['(string)'], {
|
||||
value: this.builder(true).join('')
|
||||
}));
|
||||
this.pop();
|
||||
return index + 1;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
},
|
||||
number: function (str, index) {
|
||||
var character = str[index];
|
||||
if (!NUMBER.test(character)) {
|
||||
this.tokens.push(create(symbols['(number)'], {
|
||||
value: parseFloat(this.builder(true).join(''))
|
||||
}));
|
||||
this.pop();
|
||||
return index;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
},
|
||||
name: function (str, index) {
|
||||
var character = str[index];
|
||||
if (!NAME.test(character)) {
|
||||
var name = this.builder(true).join('');
|
||||
var symbol = symbols[name];
|
||||
if (symbol) {
|
||||
if (name === 'in' && this.tokens[this.tokens.length-1].id === 'not') {
|
||||
symbol = symbols['not in'];
|
||||
this.tokens.pop();
|
||||
} else if (name === 'not' && this.tokens[this.tokens.length-1].id === 'is') {
|
||||
symbol = symbols['is not'];
|
||||
this.tokens.pop();
|
||||
}
|
||||
this.tokens.push(create(symbol));
|
||||
} else {
|
||||
this.tokens.push(create(symbols['(name)'], {
|
||||
value: name
|
||||
}));
|
||||
}
|
||||
this.pop();
|
||||
return index;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
}
|
||||
};
|
||||
|
||||
exports.tokenize = function tokenize(str) {
|
||||
var index = 0,
|
||||
tokenizer = new Tokenizer(str);
|
||||
str += '\0';
|
||||
|
||||
do {
|
||||
index = tokenizer.feed(str, index);
|
||||
} while (index !== str.length);
|
||||
return tokenizer.tokens;
|
||||
};
|
||||
|
||||
var token, next;
|
||||
function expression(rbp) {
|
||||
rbp = rbp || 0;
|
||||
var t = token;
|
||||
token = next();
|
||||
var left = t.nud();
|
||||
while (rbp < token.lbp) {
|
||||
t = token;
|
||||
token = next();
|
||||
left = t.led(left);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
function advance(id) {
|
||||
if (id && token.id !== id) {
|
||||
throw new Error(
|
||||
'Expected "' + id + '", got "' + token.id + '"');
|
||||
}
|
||||
token = next();
|
||||
}
|
||||
|
||||
exports.object = create({}, {});
|
||||
exports.bool = function (arg) { return !!arg; };
|
||||
exports.tuple = create(exports.object, {
|
||||
__contains__: function (value) {
|
||||
for(var i=0, len=this.values.length; i<len; ++i) {
|
||||
if (this.values[i] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this.values;
|
||||
}
|
||||
});
|
||||
exports.list = exports.tuple;
|
||||
exports.dict = create(exports.object, {
|
||||
toJSON: function () {
|
||||
return this.values;
|
||||
}
|
||||
});
|
||||
|
||||
exports.parse = function (toks) {
|
||||
var index = 0;
|
||||
token = toks[0];
|
||||
next = function () { return toks[++index]; };
|
||||
return expression();
|
||||
};
|
||||
var evaluate_operator = function (operator, a, b) {
|
||||
switch (operator) {
|
||||
case '==': case 'is': return a === b;
|
||||
case '!=': case 'is not': return a !== b;
|
||||
case '<': return a < b;
|
||||
case '<=': return a <= b;
|
||||
case '>': return a > b;
|
||||
case '>=': return a >= b;
|
||||
case 'in':
|
||||
if (typeof b === 'string') {
|
||||
return b.indexOf(a) !== -1;
|
||||
}
|
||||
return b.__contains__(a);
|
||||
case 'not in':
|
||||
if (typeof b === 'string') {
|
||||
return b.indexOf(a) === -1;
|
||||
}
|
||||
return !b.__contains__(a);
|
||||
}
|
||||
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
|
||||
};
|
||||
exports.evaluate = function (expr, context) {
|
||||
switch (expr.id) {
|
||||
case '(name)':
|
||||
var val = context[expr.value];
|
||||
if (val === undefined) {
|
||||
throw new Error("NameError: name '" + expr.value + "' is not defined");
|
||||
}
|
||||
return val;
|
||||
case '(string)':
|
||||
case '(number)':
|
||||
return expr.value;
|
||||
case '(constant)':
|
||||
if (expr.value === 'None')
|
||||
return null;
|
||||
else if (expr.value === 'False')
|
||||
return false;
|
||||
else if (expr.value === 'True')
|
||||
return true;
|
||||
throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
|
||||
case '(comparator)':
|
||||
var result, left = exports.evaluate(expr.expressions[0], context);
|
||||
for(var i=0; i<expr.operators.length; ++i) {
|
||||
result = evaluate_operator(
|
||||
expr.operators[i],
|
||||
left,
|
||||
left = exports.evaluate(expr.expressions[i+1], context));
|
||||
if (!result) { return false; }
|
||||
}
|
||||
return true;
|
||||
case '-':
|
||||
if (expr.second) {
|
||||
throw new Error('SyntaxError: binary [-] not implemented yet');
|
||||
}
|
||||
return -(exports.evaluate(expr.first, context));
|
||||
case 'not':
|
||||
return !(exports.evaluate(expr.first, context));
|
||||
case 'and':
|
||||
return (exports.evaluate(expr.first, context)
|
||||
&& exports.evaluate(expr.second, context));
|
||||
case 'or':
|
||||
return (exports.evaluate(expr.first, context)
|
||||
|| exports.evaluate(expr.second, context));
|
||||
case '(':
|
||||
if (expr.second) {
|
||||
var fn = exports.evaluate(expr.first, context), args=[];
|
||||
for (var jj=0; jj<expr.second.length; ++jj) {
|
||||
args.push(exports.evaluate(
|
||||
expr.second[jj], context));
|
||||
}
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
var tuple_exprs = expr.first,
|
||||
tuple_values = [];
|
||||
for (var j=0, len=tuple_exprs.length; j<len; ++j) {
|
||||
tuple_values.push(exports.evaluate(
|
||||
tuple_exprs[j], context));
|
||||
}
|
||||
return create(exports.tuple, {values: tuple_values});
|
||||
case '[':
|
||||
if (expr.second) {
|
||||
throw new Error('SyntaxError: indexing not implemented yet');
|
||||
}
|
||||
var list_exprs = expr.first, list_values = [];
|
||||
for (var k=0; k<list_exprs.length; ++k) {
|
||||
list_values.push(exports.evaluate(
|
||||
list_exprs[k], context));
|
||||
}
|
||||
return create(exports.list, {values: list_values});
|
||||
case '{':
|
||||
var dict_exprs = expr.first, dict_values = {};
|
||||
for(var l=0; l<dict_exprs.length; ++l) {
|
||||
dict_values[exports.evaluate(dict_exprs[l][0], context)] =
|
||||
exports.evaluate(dict_exprs[l][1], context);
|
||||
}
|
||||
return create(exports.dict, {values: dict_values});
|
||||
case '.':
|
||||
if (expr.second.id !== '(name)') {
|
||||
throw new Error('SyntaxError: ' + expr);
|
||||
}
|
||||
return exports.evaluate(expr.first, context)[expr.second.value];
|
||||
default:
|
||||
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
|
||||
}
|
||||
};
|
||||
exports.eval = function (str, context) {
|
||||
return exports.evaluate(
|
||||
exports.parse(
|
||||
exports.tokenize(
|
||||
str)),
|
||||
context);
|
||||
}
|
||||
})(typeof exports === 'undefined' ? py : exports);
|
||||
@@ -0,0 +1,90 @@
|
||||
var py = require('../lib/py.js'),
|
||||
assert = require('assert');
|
||||
|
||||
// Literals
|
||||
assert.strictEqual(py.eval('1'), 1);
|
||||
assert.strictEqual(py.eval('None'), null);
|
||||
assert.strictEqual(py.eval('False'), false);
|
||||
assert.strictEqual(py.eval('True'), true);
|
||||
assert.strictEqual(py.eval('"somestring"'), 'somestring');
|
||||
assert.strictEqual(py.eval("'somestring'"), 'somestring');
|
||||
assert.deepEqual(py.eval("()").toJSON(), []);
|
||||
assert.deepEqual(py.eval("[]").toJSON(), []);
|
||||
assert.deepEqual(py.eval("{}").toJSON(), {});
|
||||
assert.deepEqual(py.eval("(None, True, False, 0, 1, 'foo')").toJSON(),
|
||||
[null, true, false, 0, 1, 'foo']);
|
||||
assert.deepEqual(py.eval("[None, True, False, 0, 1, 'foo']").toJSON(),
|
||||
[null, true, false, 0, 1, 'foo']);
|
||||
assert.deepEqual(py.eval("{'foo': 1, foo: 2}", {foo: 'bar'}).toJSON(),
|
||||
{foo: 1, bar: 2});
|
||||
|
||||
// Equality tests
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo'", {foo: 'foo'}));
|
||||
// Inequality
|
||||
assert.ok(py.eval(
|
||||
"foo != bar", {foo: 'foo', bar: 'bar'}));
|
||||
|
||||
// Comparisons
|
||||
assert.ok(py.eval('3 < 5'));
|
||||
assert.ok(py.eval('5 >= 3'));
|
||||
assert.ok(py.eval('3 >= 3'));
|
||||
assert.ok(!py.eval('5 < 3'));
|
||||
assert.ok(py.eval('1 < 3 < 5'));
|
||||
assert.ok(py.eval('5 > 3 > 1'));
|
||||
assert.ok(py.eval('1 < 3 > 2 == 2 > -2 not in (0, 1, 2)'));
|
||||
// string rich comparisons
|
||||
assert.ok(py.eval(
|
||||
'date >= current', {date: '2010-06-08', current: '2010-06-05'}));
|
||||
|
||||
// Boolean operators
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' or foo == 'bar'", {foo: 'bar'}));
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' and bar == 'bar'", {foo: 'foo', bar: 'bar'}));
|
||||
// - lazyness, second clauses NameError if not short-circuited
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' or bar == 'bar'", {foo: 'foo'}));
|
||||
assert.ok(!py.eval(
|
||||
"foo == 'foo' and bar == 'bar'", {foo: 'bar'}));
|
||||
|
||||
// contains (in)
|
||||
assert.ok(py.eval(
|
||||
"foo in ('foo', 'bar')", {foo: 'bar'}));
|
||||
assert.ok(py.eval('1 in (1, 2, 3, 4)'));
|
||||
assert.ok(!py.eval('1 in (2, 3, 4)'));
|
||||
assert.ok(py.eval('type in ("url",)', {type: 'url'}));
|
||||
assert.ok(!py.eval('type in ("url",)', {type: 'ur'}));
|
||||
assert.ok(py.eval('1 not in (2, 3, 4)'));
|
||||
assert.ok(py.eval('type not in ("url",)', {type: 'ur'}));
|
||||
|
||||
assert.ok(py.eval(
|
||||
"foo in ['foo', 'bar']", {foo: 'bar'}));
|
||||
// string contains
|
||||
assert.ok(py.eval('type in "view"', {type: 'view'}));
|
||||
assert.ok(!py.eval('type in "view"', {type: 'bob'}));
|
||||
assert.ok(py.eval('type in "url"', {type: 'ur'}));
|
||||
|
||||
// Literals
|
||||
assert.strictEqual(py.eval('False'), false);
|
||||
assert.strictEqual(py.eval('True'), true);
|
||||
assert.strictEqual(py.eval('None'), null);
|
||||
assert.ok(py.eval('foo == False', {foo: false}));
|
||||
assert.ok(!py.eval('foo == False', {foo: true}));
|
||||
|
||||
// conversions
|
||||
assert.strictEqual(
|
||||
py.eval('bool(date_deadline)', {bool: py.bool, date_deadline: '2008'}),
|
||||
true);
|
||||
|
||||
// getattr
|
||||
assert.ok(py.eval('foo.bar', {foo: {bar: true}}));
|
||||
assert.ok(!py.eval('foo.bar', {foo: {bar: false}}));
|
||||
|
||||
// complex expressions
|
||||
assert.ok(py.eval(
|
||||
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
|
||||
{state: 'pending', date_deadline: false}));
|
||||
assert.ok(py.eval(
|
||||
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
|
||||
{state: 'pending', date_deadline: '2010-05-08', current_date: '2010-05-08'}));;
|
||||
@@ -4,301 +4,436 @@
|
||||
// Documentation: https://github.com/edtsech/underscore.string
|
||||
// Some code is borrowed from MooTools and Alexandru Marasteanu.
|
||||
|
||||
// Version 1.1.4
|
||||
// Version 1.1.6
|
||||
|
||||
(function(){
|
||||
// ------------------------- Baseline setup ---------------------------------
|
||||
|
||||
// Establish the root object, "window" in the browser, or "global" on the server.
|
||||
var root = this;
|
||||
(function(root){
|
||||
'use strict';
|
||||
|
||||
var nativeTrim = String.prototype.trim;
|
||||
if (typeof _ != 'undefined') {
|
||||
var _reverse = _().reverse,
|
||||
_include = _.include;
|
||||
}
|
||||
|
||||
function str_repeat(i, m) {
|
||||
for (var o = []; m > 0; o[--m] = i);
|
||||
return o.join('');
|
||||
// Defining helper functions.
|
||||
|
||||
var nativeTrim = String.prototype.trim;
|
||||
|
||||
var parseNumber = function(source) { return source * 1 || 0; };
|
||||
|
||||
var strRepeat = function(i, m) {
|
||||
for (var o = []; m > 0; o[--m] = i);
|
||||
return o.join('');
|
||||
};
|
||||
|
||||
var slice = function(a){
|
||||
return Array.prototype.slice.call(a);
|
||||
};
|
||||
|
||||
var defaultToWhiteSpace = function(characters){
|
||||
if (characters) {
|
||||
return _s.escapeRegExp(characters);
|
||||
}
|
||||
return '\\s';
|
||||
};
|
||||
|
||||
var sArgs = function(method){
|
||||
return function(){
|
||||
var args = slice(arguments);
|
||||
for(var i=0; i<args.length; i++)
|
||||
args[i] = args[i] == null ? '' : '' + args[i];
|
||||
return method.apply(null, args);
|
||||
};
|
||||
};
|
||||
|
||||
// sprintf() for JavaScript 0.7-beta1
|
||||
// http://www.diveintojavascript.com/projects/javascript-sprintf
|
||||
//
|
||||
// Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
|
||||
// All rights reserved.
|
||||
|
||||
var sprintf = (function() {
|
||||
function get_type(variable) {
|
||||
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
|
||||
}
|
||||
|
||||
function defaultToWhiteSpace(characters){
|
||||
if (characters) {
|
||||
return _s.escapeRegExp(characters);
|
||||
var str_repeat = strRepeat;
|
||||
|
||||
var str_format = function() {
|
||||
if (!str_format.cache.hasOwnProperty(arguments[0])) {
|
||||
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
|
||||
}
|
||||
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
|
||||
};
|
||||
|
||||
str_format.format = function(parse_tree, argv) {
|
||||
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
|
||||
for (i = 0; i < tree_length; i++) {
|
||||
node_type = get_type(parse_tree[i]);
|
||||
if (node_type === 'string') {
|
||||
output.push(parse_tree[i]);
|
||||
}
|
||||
return '\\s';
|
||||
}
|
||||
|
||||
var _s = {
|
||||
|
||||
isBlank: function(str){
|
||||
return !!str.match(/^\s*$/);
|
||||
},
|
||||
|
||||
capitalize : function(str) {
|
||||
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
|
||||
},
|
||||
|
||||
chop: function(str, step){
|
||||
step = step || str.length;
|
||||
var arr = [];
|
||||
for (var i = 0; i < str.length;) {
|
||||
arr.push(str.slice(i,i + step));
|
||||
i = i + step;
|
||||
else if (node_type === 'array') {
|
||||
match = parse_tree[i]; // convenience purposes only
|
||||
if (match[2]) { // keyword argument
|
||||
arg = argv[cursor];
|
||||
for (k = 0; k < match[2].length; k++) {
|
||||
if (!arg.hasOwnProperty(match[2][k])) {
|
||||
throw(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
|
||||
}
|
||||
arg = arg[match[2][k]];
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
} else if (match[1]) { // positional argument (explicit)
|
||||
arg = argv[match[1]];
|
||||
}
|
||||
else { // positional argument (implicit)
|
||||
arg = argv[cursor++];
|
||||
}
|
||||
|
||||
clean: function(str){
|
||||
return _s.strip(str.replace(/\s+/g, ' '));
|
||||
},
|
||||
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
|
||||
throw(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
|
||||
}
|
||||
switch (match[8]) {
|
||||
case 'b': arg = arg.toString(2); break;
|
||||
case 'c': arg = String.fromCharCode(arg); break;
|
||||
case 'd': arg = parseInt(arg, 10); break;
|
||||
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
|
||||
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
|
||||
case 'o': arg = arg.toString(8); break;
|
||||
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
|
||||
case 'u': arg = Math.abs(arg); break;
|
||||
case 'x': arg = arg.toString(16); break;
|
||||
case 'X': arg = arg.toString(16).toUpperCase(); break;
|
||||
}
|
||||
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
|
||||
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
|
||||
pad_length = match[6] - String(arg).length;
|
||||
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
|
||||
output.push(match[5] ? arg + pad : pad + arg);
|
||||
}
|
||||
}
|
||||
return output.join('');
|
||||
};
|
||||
|
||||
count: function(str, substr){
|
||||
var count = 0, index;
|
||||
for (var i=0; i < str.length;) {
|
||||
index = str.indexOf(substr, i);
|
||||
index >= 0 && count++;
|
||||
i = i + (index >= 0 ? index : 0) + substr.length;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
str_format.cache = {};
|
||||
|
||||
chars: function(str) {
|
||||
return str.split('');
|
||||
},
|
||||
|
||||
escapeHTML: function(str) {
|
||||
return String(str||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
},
|
||||
|
||||
unescapeHTML: function(str) {
|
||||
return String(str||'').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
},
|
||||
|
||||
escapeRegExp: function(str){
|
||||
// From MooTools core 1.2.4
|
||||
return String(str||'').replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||
},
|
||||
|
||||
insert: function(str, i, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(i, 0, substr);
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
includes: function(str, needle){
|
||||
return str.indexOf(needle) !== -1;
|
||||
},
|
||||
|
||||
join: function(sep) {
|
||||
// TODO: Could this be faster by converting
|
||||
// arguments to Array and using array.join(sep)?
|
||||
sep = String(sep);
|
||||
var str = "";
|
||||
for (var i=1; i < arguments.length; i += 1) {
|
||||
str += String(arguments[i]);
|
||||
if ( i !== arguments.length-1 ) {
|
||||
str += sep;
|
||||
str_format.parse = function(fmt) {
|
||||
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
|
||||
while (_fmt) {
|
||||
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
|
||||
parse_tree.push(match[0]);
|
||||
}
|
||||
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
|
||||
parse_tree.push('%');
|
||||
}
|
||||
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
|
||||
if (match[2]) {
|
||||
arg_names |= 1;
|
||||
var field_list = [], replacement_field = match[2], field_match = [];
|
||||
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
||||
field_list.push(field_match[1]);
|
||||
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
|
||||
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
||||
field_list.push(field_match[1]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
lines: function(str) {
|
||||
return str.split("\n");
|
||||
},
|
||||
|
||||
// reverse: function(str){
|
||||
// return Array.prototype.reverse.apply(str.split('')).join('');
|
||||
// },
|
||||
|
||||
splice: function(str, i, howmany, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(i, howmany, substr);
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
startsWith: function(str, starts){
|
||||
return str.length >= starts.length && str.substring(0, starts.length) === starts;
|
||||
},
|
||||
|
||||
endsWith: function(str, ends){
|
||||
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
|
||||
},
|
||||
|
||||
succ: function(str){
|
||||
var arr = str.split('');
|
||||
arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
titleize: function(str){
|
||||
var arr = str.split(' '),
|
||||
word;
|
||||
for (var i=0; i < arr.length; i++) {
|
||||
word = arr[i].split('');
|
||||
if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
|
||||
i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
|
||||
}
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
camelize: function(str){
|
||||
return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
},
|
||||
|
||||
underscored: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
|
||||
},
|
||||
|
||||
dasherize: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
|
||||
},
|
||||
|
||||
trim: function(str, characters){
|
||||
if (!characters && nativeTrim) {
|
||||
return nativeTrim.call(str);
|
||||
}
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
ltrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
|
||||
},
|
||||
|
||||
rtrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
truncate: function(str, length, truncateStr){
|
||||
truncateStr = truncateStr || '...';
|
||||
return str.slice(0,length) + truncateStr;
|
||||
},
|
||||
|
||||
words: function(str, delimiter) {
|
||||
delimiter = delimiter || " ";
|
||||
return str.split(delimiter);
|
||||
},
|
||||
|
||||
|
||||
pad: function(str, length, padStr, type) {
|
||||
|
||||
var padding = '';
|
||||
var padlen = 0;
|
||||
|
||||
if (!padStr) { padStr = ' '; }
|
||||
else if (padStr.length > 1) { padStr = padStr[0]; }
|
||||
switch(type) {
|
||||
case "right":
|
||||
padlen = (length - str.length);
|
||||
padding = str_repeat(padStr, padlen);
|
||||
str = str+padding;
|
||||
break;
|
||||
case "both":
|
||||
padlen = (length - str.length);
|
||||
padding = {
|
||||
'left' : str_repeat(padStr, Math.ceil(padlen/2)),
|
||||
'right': str_repeat(padStr, Math.floor(padlen/2))
|
||||
};
|
||||
str = padding.left+str+padding.right;
|
||||
break;
|
||||
default: // "left"
|
||||
padlen = (length - str.length);
|
||||
padding = str_repeat(padStr, padlen);;
|
||||
str = padding+str;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
lpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr);
|
||||
},
|
||||
|
||||
rpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'right');
|
||||
},
|
||||
|
||||
lrpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'both');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Credits for this function goes to
|
||||
* http://www.diveintojavascript.com/projects/sprintf-for-javascript
|
||||
*
|
||||
* Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
|
||||
* All rights reserved.
|
||||
* */
|
||||
sprintf: function(){
|
||||
|
||||
var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
|
||||
while (f) {
|
||||
if (m = /^[^\x25]+/.exec(f)) {
|
||||
o.push(m[0]);
|
||||
}
|
||||
else if (m = /^\x25{2}/.exec(f)) {
|
||||
o.push('%');
|
||||
}
|
||||
else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
|
||||
if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
|
||||
throw('Too few arguments.');
|
||||
}
|
||||
if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
|
||||
throw('Expecting number but found ' + typeof(a));
|
||||
}
|
||||
switch (m[7]) {
|
||||
case 'b': a = a.toString(2); break;
|
||||
case 'c': a = String.fromCharCode(a); break;
|
||||
case 'd': a = parseInt(a); break;
|
||||
case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
|
||||
case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
|
||||
case 'o': a = a.toString(8); break;
|
||||
case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
|
||||
case 'u': a = Math.abs(a); break;
|
||||
case 'x': a = a.toString(16); break;
|
||||
case 'X': a = a.toString(16).toUpperCase(); break;
|
||||
}
|
||||
a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
|
||||
c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
|
||||
x = m[5] - String(a).length - s.length;
|
||||
p = m[5] ? str_repeat(c, x) : '';
|
||||
o.push(s + (m[4] ? a + p : p + a));
|
||||
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
|
||||
field_list.push(field_match[1]);
|
||||
}
|
||||
else {
|
||||
throw('Huh ?!');
|
||||
throw('[_.sprintf] huh?');
|
||||
}
|
||||
f = f.substring(m[0].length);
|
||||
}
|
||||
}
|
||||
return o.join('');
|
||||
else {
|
||||
throw('[_.sprintf] huh?');
|
||||
}
|
||||
match[2] = field_list;
|
||||
}
|
||||
else {
|
||||
arg_names |= 2;
|
||||
}
|
||||
if (arg_names === 3) {
|
||||
throw('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
|
||||
}
|
||||
parse_tree.push(match);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw('[_.sprintf] huh?');
|
||||
}
|
||||
_fmt = _fmt.substring(match[0].length);
|
||||
}
|
||||
return parse_tree;
|
||||
};
|
||||
|
||||
// Aliases
|
||||
return str_format;
|
||||
})();
|
||||
|
||||
_s.strip = _s.trim;
|
||||
_s.lstrip = _s.ltrim;
|
||||
_s.rstrip = _s.rtrim;
|
||||
_s.center = _s.lrpad
|
||||
_s.ljust = _s.lpad
|
||||
_s.rjust = _s.rpad
|
||||
|
||||
// CommonJS module is defined
|
||||
if (typeof window === 'undefined' && typeof module !== 'undefined') {
|
||||
// Export module
|
||||
module.exports = _s;
|
||||
|
||||
// Integrate with Underscore.js
|
||||
} else if (typeof root._ !== 'undefined') {
|
||||
root._.mixin(_s);
|
||||
// Defining underscore.string
|
||||
|
||||
// Or define it
|
||||
} else {
|
||||
root._ = _s;
|
||||
}
|
||||
var _s = {
|
||||
|
||||
}());
|
||||
isBlank: sArgs(function(str){
|
||||
return (/^\s*$/).test(str);
|
||||
}),
|
||||
|
||||
stripTags: sArgs(function(str){
|
||||
return str.replace(/<\/?[^>]+>/ig, '');
|
||||
}),
|
||||
|
||||
capitalize : sArgs(function(str) {
|
||||
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
|
||||
}),
|
||||
|
||||
chop: sArgs(function(str, step){
|
||||
step = parseNumber(step) || str.length;
|
||||
var arr = [];
|
||||
for (var i = 0; i < str.length;) {
|
||||
arr.push(str.slice(i,i + step));
|
||||
i = i + step;
|
||||
}
|
||||
return arr;
|
||||
}),
|
||||
|
||||
clean: sArgs(function(str){
|
||||
return _s.strip(str.replace(/\s+/g, ' '));
|
||||
}),
|
||||
|
||||
count: sArgs(function(str, substr){
|
||||
var count = 0, index;
|
||||
for (var i=0; i < str.length;) {
|
||||
index = str.indexOf(substr, i);
|
||||
index >= 0 && count++;
|
||||
i = i + (index >= 0 ? index : 0) + substr.length;
|
||||
}
|
||||
return count;
|
||||
}),
|
||||
|
||||
chars: sArgs(function(str) {
|
||||
return str.split('');
|
||||
}),
|
||||
|
||||
escapeHTML: sArgs(function(str) {
|
||||
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
}),
|
||||
|
||||
unescapeHTML: sArgs(function(str) {
|
||||
return str.replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&');
|
||||
}),
|
||||
|
||||
escapeRegExp: sArgs(function(str){
|
||||
// From MooTools core 1.2.4
|
||||
return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||
}),
|
||||
|
||||
insert: sArgs(function(str, i, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(parseNumber(i), 0, substr);
|
||||
return arr.join('');
|
||||
}),
|
||||
|
||||
includes: sArgs(function(str, needle){
|
||||
return str.indexOf(needle) !== -1;
|
||||
}),
|
||||
|
||||
include: function(obj, needle) {
|
||||
if (!_include || (/string|number/).test(typeof obj)) {
|
||||
return this.includes(obj, needle);
|
||||
} else {
|
||||
return _include(obj, needle);
|
||||
}
|
||||
},
|
||||
|
||||
join: sArgs(function(sep) {
|
||||
var args = slice(arguments);
|
||||
return args.join(args.shift());
|
||||
}),
|
||||
|
||||
lines: sArgs(function(str) {
|
||||
return str.split("\n");
|
||||
}),
|
||||
|
||||
reverse: function(obj){
|
||||
if (!_reverse || (/string|number/).test(typeof obj)) {
|
||||
return Array.prototype.reverse.apply(String(obj).split('')).join('');
|
||||
} else {
|
||||
return _reverse.call(_(obj));
|
||||
}
|
||||
},
|
||||
|
||||
splice: sArgs(function(str, i, howmany, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(parseNumber(i), parseNumber(howmany), substr);
|
||||
return arr.join('');
|
||||
}),
|
||||
|
||||
startsWith: sArgs(function(str, starts){
|
||||
return str.length >= starts.length && str.substring(0, starts.length) === starts;
|
||||
}),
|
||||
|
||||
endsWith: sArgs(function(str, ends){
|
||||
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
|
||||
}),
|
||||
|
||||
succ: sArgs(function(str){
|
||||
var arr = str.split('');
|
||||
arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
|
||||
return arr.join('');
|
||||
}),
|
||||
|
||||
titleize: sArgs(function(str){
|
||||
var arr = str.split(' '),
|
||||
word;
|
||||
for (var i=0; i < arr.length; i++) {
|
||||
word = arr[i].split('');
|
||||
if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
|
||||
i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
|
||||
}
|
||||
return arr.join('');
|
||||
}),
|
||||
|
||||
camelize: sArgs(function(str){
|
||||
return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
}),
|
||||
|
||||
underscored: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
|
||||
},
|
||||
|
||||
dasherize: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
|
||||
},
|
||||
|
||||
trim: sArgs(function(str, characters){
|
||||
if (!characters && nativeTrim) {
|
||||
return nativeTrim.call(str);
|
||||
}
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
|
||||
}),
|
||||
|
||||
ltrim: sArgs(function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
|
||||
}),
|
||||
|
||||
rtrim: sArgs(function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
|
||||
}),
|
||||
|
||||
truncate: sArgs(function(str, length, truncateStr){
|
||||
truncateStr = truncateStr || '...';
|
||||
length = parseNumber(length);
|
||||
return str.length > length ? str.slice(0,length) + truncateStr : str;
|
||||
}),
|
||||
|
||||
words: function(str, delimiter) {
|
||||
return String(str).split(delimiter || " ");
|
||||
},
|
||||
|
||||
pad: sArgs(function(str, length, padStr, type) {
|
||||
var padding = '',
|
||||
padlen = 0;
|
||||
|
||||
length = parseNumber(length);
|
||||
|
||||
if (!padStr) { padStr = ' '; }
|
||||
else if (padStr.length > 1) { padStr = padStr.charAt(0); }
|
||||
switch(type) {
|
||||
case 'right':
|
||||
padlen = (length - str.length);
|
||||
padding = strRepeat(padStr, padlen);
|
||||
str = str+padding;
|
||||
break;
|
||||
case 'both':
|
||||
padlen = (length - str.length);
|
||||
padding = {
|
||||
'left' : strRepeat(padStr, Math.ceil(padlen/2)),
|
||||
'right': strRepeat(padStr, Math.floor(padlen/2))
|
||||
};
|
||||
str = padding.left+str+padding.right;
|
||||
break;
|
||||
default: // 'left'
|
||||
padlen = (length - str.length);
|
||||
padding = strRepeat(padStr, padlen);;
|
||||
str = padding+str;
|
||||
}
|
||||
return str;
|
||||
}),
|
||||
|
||||
lpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr);
|
||||
},
|
||||
|
||||
rpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'right');
|
||||
},
|
||||
|
||||
lrpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'both');
|
||||
},
|
||||
|
||||
sprintf: sprintf,
|
||||
|
||||
vsprintf: function(fmt, argv){
|
||||
argv.unshift(fmt);
|
||||
return sprintf.apply(null, argv);
|
||||
},
|
||||
|
||||
toNumber: function(str, decimals) {
|
||||
var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
|
||||
return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
|
||||
},
|
||||
|
||||
strRight: sArgs(function(sourceStr, sep){
|
||||
var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
|
||||
return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
|
||||
}),
|
||||
|
||||
strRightBack: sArgs(function(sourceStr, sep){
|
||||
var pos = (!sep) ? -1 : sourceStr.lastIndexOf(sep);
|
||||
return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
|
||||
}),
|
||||
|
||||
strLeft: sArgs(function(sourceStr, sep){
|
||||
var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
|
||||
return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
|
||||
}),
|
||||
|
||||
strLeftBack: sArgs(function(sourceStr, sep){
|
||||
var pos = sourceStr.lastIndexOf(sep);
|
||||
return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// Aliases
|
||||
|
||||
_s.strip = _s.trim;
|
||||
_s.lstrip = _s.ltrim;
|
||||
_s.rstrip = _s.rtrim;
|
||||
_s.center = _s.lrpad;
|
||||
_s.ljust = _s.lpad;
|
||||
_s.rjust = _s.rpad;
|
||||
|
||||
// CommonJS module is defined
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
// Export module
|
||||
module.exports = _s;
|
||||
|
||||
// Integrate with Underscore.js
|
||||
} else if (typeof root._ !== 'undefined') {
|
||||
root._.mixin(_s);
|
||||
|
||||
// Or define it
|
||||
} else {
|
||||
root._ = _s;
|
||||
}
|
||||
|
||||
}(this || window));
|
||||
|
||||
@@ -125,7 +125,7 @@ body.openerp, .openerp textarea, .openerp input, .openerp select, .openerp optio
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.openerp .login.login_invalid .login_error_message {
|
||||
.openerp .login .login_invalid .login_error_message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*---------------------------------------------------------
|
||||
* OpenERP Web chrome
|
||||
*---------------------------------------------------------*/
|
||||
|
||||
openerp.web.chrome = function(openerp) {
|
||||
var QWeb = openerp.web.qweb;
|
||||
|
||||
@@ -129,6 +128,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
|
||||
// Destroy widget
|
||||
this.close();
|
||||
this.$dialog.dialog('destroy');
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -253,7 +253,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
.removeClass("database_block")
|
||||
.end()
|
||||
.empty();
|
||||
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
* Converts a .serializeArray() result into a dict. Does not bother folding
|
||||
@@ -299,9 +299,9 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
|
||||
var admin = result[1][0];
|
||||
setTimeout(function () {
|
||||
self.stop();
|
||||
self.widget_parent.do_login(
|
||||
info.db, admin.login, admin.password);
|
||||
self.stop();
|
||||
$.unblockUI();
|
||||
});
|
||||
});
|
||||
@@ -463,6 +463,9 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
|
||||
openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
remember_creditentials: true,
|
||||
|
||||
template: "Login",
|
||||
identifier_prefix: 'oe-app-login-',
|
||||
/**
|
||||
* @constructs openerp.web.Login
|
||||
* @extends openerp.web.Widget
|
||||
@@ -471,8 +474,8 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
* @param element_id
|
||||
*/
|
||||
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
init: function(parent) {
|
||||
this._super(parent);
|
||||
this.has_local_storage = typeof(localStorage) != 'undefined';
|
||||
this.selected_db = null;
|
||||
this.selected_login = null;
|
||||
@@ -487,17 +490,6 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.rpc("/web/database/get_list", {}, function(result) {
|
||||
self.db_list = result.db_list;
|
||||
self.display();
|
||||
}, function() {
|
||||
self.display();
|
||||
});
|
||||
},
|
||||
display: function() {
|
||||
var self = this;
|
||||
|
||||
this.$element.html(QWeb.render("Login", this));
|
||||
this.database = new openerp.web.Database(
|
||||
this, "oe_database", "oe_db_options");
|
||||
|
||||
@@ -506,6 +498,17 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
});
|
||||
|
||||
this.$element.find("form").submit(this.on_submit);
|
||||
|
||||
this.rpc("/web/database/get_list", {}, function(result) {
|
||||
var tpl = openerp.web.qweb.render('Login_dblist', {db_list: result.db_list, selected_db: self.selected_db});
|
||||
self.$element.find("input[name=db]").replaceWith(tpl)
|
||||
},
|
||||
function(error, event) {
|
||||
if (error.data.fault_code === 'AccessDenied') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
on_login_invalid: function() {
|
||||
this.$element.closest(".openerp").addClass("login-mode");
|
||||
@@ -933,7 +936,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||
openerp.web.Widget.prototype.notification = new openerp.web.Notification(this, "oe_notification");
|
||||
|
||||
this.header = new openerp.web.Header(this);
|
||||
this.login = new openerp.web.Login(this, "oe_login");
|
||||
this.login = new openerp.web.Login(this);
|
||||
this.header.on_logout.add(this.login.on_logout);
|
||||
this.header.on_action.add(this.on_menu_action);
|
||||
|
||||
@@ -953,7 +956,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||
start: function() {
|
||||
this.header.appendTo($("#oe_header"));
|
||||
this.session.start();
|
||||
this.login.start();
|
||||
this.login.appendTo($('#oe_login'));
|
||||
this.menu.start();
|
||||
},
|
||||
on_logged: function() {
|
||||
|
||||
@@ -233,6 +233,7 @@ openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends
|
||||
});
|
||||
|
||||
openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet# */{
|
||||
identifier_prefix: "dataset",
|
||||
/**
|
||||
* DateaManagement interface between views and the collection of selected
|
||||
* OpenERP records (represents the view's state?)
|
||||
@@ -242,16 +243,12 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet
|
||||
*
|
||||
* @param {String} model the OpenERP model this dataset will manage
|
||||
*/
|
||||
init: function(source_controller, model, context) {
|
||||
// we don't want the dataset to be a child of anything!
|
||||
this._super(null);
|
||||
this.session = source_controller ? source_controller.session : undefined;
|
||||
init: function(parent, model, context) {
|
||||
this._super(parent);
|
||||
this.model = model;
|
||||
this.context = context || {};
|
||||
this.index = null;
|
||||
},
|
||||
start: function() {
|
||||
},
|
||||
previous: function () {
|
||||
this.index -= 1;
|
||||
if (this.index < 0) {
|
||||
@@ -549,13 +546,11 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
|
||||
sort: this.sort(),
|
||||
offset: offset,
|
||||
limit: options.limit || false
|
||||
}, function (result) {
|
||||
}).pipe(function (result) {
|
||||
self.ids = result.ids;
|
||||
self.offset = offset;
|
||||
if (callback) {
|
||||
callback(result.records);
|
||||
}
|
||||
});
|
||||
return result.records;
|
||||
}).then(callback);
|
||||
},
|
||||
get_domain: function (other_domain) {
|
||||
if (other_domain) {
|
||||
@@ -603,7 +598,6 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
|
||||
});
|
||||
openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
virtual_id_prefix: "one2many_v_id_",
|
||||
virtual_id_regex: /one2many_v_id_.*/,
|
||||
debug_mode: true,
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -724,6 +718,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
return completion.promise();
|
||||
}
|
||||
});
|
||||
openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
|
||||
|
||||
openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({
|
||||
default_get: function(fields, callback) {
|
||||
return this._super(fields, callback).then(this.on_default_get);
|
||||
|
||||
@@ -41,7 +41,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
this.has_been_loaded = $.Deferred();
|
||||
this.$form_header = null;
|
||||
this.translatable_fields = [];
|
||||
_.defaults(this.options, {"always_show_new_button": true});
|
||||
_.defaults(this.options, {"always_show_new_button": true,
|
||||
"not_interactible_on_create": false});
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
@@ -72,17 +73,25 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
_.each(this.widgets, function(w) {
|
||||
w.stop();
|
||||
});
|
||||
this._super();
|
||||
},
|
||||
reposition: function ($e) {
|
||||
this.$element = $e;
|
||||
this.on_loaded();
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
var self = this;
|
||||
this.fields_view = data;
|
||||
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
||||
if (data) {
|
||||
this.fields_view = data;
|
||||
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
||||
|
||||
this.$element.html(QWeb.render(this.form_template, { 'frame': frame, 'view': this }));
|
||||
this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'view': this });
|
||||
}
|
||||
this.$element.html(this.rendered);
|
||||
_.each(this.widgets, function(w) {
|
||||
w.start();
|
||||
});
|
||||
this.$form_header = this.$element.find('#' + this.element_id + '_header');
|
||||
this.$form_header = this.$element.find('.oe_form_header:first');
|
||||
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
||||
var action = $(this).data('pager-action');
|
||||
self.on_pager_action(action);
|
||||
@@ -93,6 +102,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
|
||||
this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
|
||||
this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
|
||||
this.$form_header.find('button.oe_form_button_toggle').click(function () {
|
||||
self.translatable_fields = [];
|
||||
self.widgets = {};
|
||||
self.fields = {};
|
||||
self.$form_header.find('button').unbind('click');
|
||||
self.registry = self.registry === openerp.web.form.widgets
|
||||
? openerp.web.form.readonly
|
||||
: openerp.web.form.widgets;
|
||||
self.on_loaded(self.fields_view);
|
||||
self.reload();
|
||||
});
|
||||
|
||||
if (this.options.sidebar && this.options.sidebar_id) {
|
||||
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
|
||||
@@ -196,7 +216,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
}
|
||||
},
|
||||
do_update_pager: function(hide_index) {
|
||||
var $pager = this.$element.find('#' + this.element_id + '_header div.oe_form_pager');
|
||||
var $pager = this.$form_header.find('div.oe_form_pager');
|
||||
var index = hide_index ? '-' : this.dataset.index + 1;
|
||||
$pager.find('span.oe_pager_index').html(index);
|
||||
$pager.find('span.oe_pager_count').html(this.dataset.ids.length);
|
||||
@@ -477,6 +497,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
if (self.dataset.parent_view)
|
||||
return self.dataset.parent_view.recursive_save();
|
||||
});
|
||||
},
|
||||
is_interactible_record: function() {
|
||||
var id = this.datarecord.id;
|
||||
if (!id) {
|
||||
if (this.options.not_interactible_on_create)
|
||||
return false;
|
||||
} else if (typeof(id) === "string") {
|
||||
if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
openerp.web.FormDialog = openerp.web.Dialog.extend({
|
||||
@@ -624,6 +655,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
|
||||
|
||||
openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{
|
||||
template: 'Widget',
|
||||
identifier_prefix: 'formview-widget-',
|
||||
/**
|
||||
* @constructs openerp.web.form.Widget
|
||||
* @extends openerp.web.Widget
|
||||
@@ -637,11 +669,13 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
|
||||
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
|
||||
this.type = this.type || node.tag;
|
||||
this.element_name = this.element_name || this.type;
|
||||
this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
|
||||
this.element_class = [
|
||||
'formview', this.view.view_id, this.element_name,
|
||||
this.view.widgets_counter++].join("_");
|
||||
|
||||
this._super(view, this.element_id);
|
||||
this._super(view);
|
||||
|
||||
this.view.widgets[this.element_id] = this;
|
||||
this.view.widgets[this.element_class] = this;
|
||||
this.children = node.children;
|
||||
this.colspan = parseInt(node.attrs.colspan || 1, 10);
|
||||
this.decrease_max_width = 0;
|
||||
@@ -654,12 +688,7 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
|
||||
this.width = this.node.attrs.width;
|
||||
},
|
||||
start: function() {
|
||||
this.$element = $('#' + this.element_id);
|
||||
},
|
||||
stop: function() {
|
||||
if (this.$element) {
|
||||
this.$element.remove();
|
||||
}
|
||||
this.$element = this.view.$element.find('.' + this.element_class);
|
||||
},
|
||||
process_modifiers: function() {
|
||||
var compute_domain = openerp.web.form.compute_domain;
|
||||
@@ -763,13 +792,24 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
var n = node.children[i];
|
||||
if (n.tag == "page") {
|
||||
var page = new openerp.web.form.WidgetNotebookPage(this.view, n, this, this.pages.length);
|
||||
var page = new openerp.web.form.WidgetNotebookPage(
|
||||
this.view, n, this, this.pages.length);
|
||||
this.pages.push(page);
|
||||
}
|
||||
}
|
||||
},
|
||||
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_last(this.do_select_first_visible_tab);
|
||||
},
|
||||
@@ -791,11 +831,11 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
||||
this.index = index;
|
||||
this.element_name = 'page_' + index;
|
||||
this._super(view, node);
|
||||
this.element_tab_id = this.element_id + '_tab';
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element_tab = $('#' + this.element_tab_id);
|
||||
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')) {
|
||||
@@ -807,9 +847,9 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetSeparator',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "WidgetSeparator";
|
||||
this.orientation = node.attrs.orientation || 'horizontal';
|
||||
if (this.orientation === 'vertical') {
|
||||
this.width = '1';
|
||||
@@ -819,9 +859,10 @@ openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetButton',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "WidgetButton";
|
||||
this.force_disabled = false;
|
||||
if (this.string) {
|
||||
// We don't have button key bindings in the webclient
|
||||
this.string = this.string.replace(/_/g, '');
|
||||
@@ -833,46 +874,74 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.click(this.on_click);
|
||||
this.$element.find("button").click(this.on_click);
|
||||
},
|
||||
on_click: function() {
|
||||
var self = this;
|
||||
this.force_disabled = true;
|
||||
this.check_disable();
|
||||
this.execute_action().always(function() {
|
||||
self.force_disabled = false;
|
||||
self.check_disable();
|
||||
});
|
||||
},
|
||||
execute_action: function() {
|
||||
var self = this;
|
||||
var exec_action = function() {
|
||||
if (self.node.attrs.confirm) {
|
||||
var def = $.Deferred();
|
||||
var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
|
||||
title: 'Confirm',
|
||||
modal: true,
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
self.on_confirmed();
|
||||
self.on_confirmed().then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
$(self).dialog("close");
|
||||
},
|
||||
Cancel: function() {
|
||||
def.resolve();
|
||||
$(self).dialog("close");
|
||||
}
|
||||
}
|
||||
});
|
||||
return def.promise();
|
||||
} else {
|
||||
self.on_confirmed();
|
||||
return self.on_confirmed();
|
||||
}
|
||||
};
|
||||
if ((!this.node.attrs.special && this.view.dirty_for_user) || !this.view.datarecord.id) {
|
||||
this.view.recursive_save().then(exec_action);
|
||||
return this.view.recursive_save().pipe(exec_action);
|
||||
} else {
|
||||
exec_action();
|
||||
return exec_action();
|
||||
}
|
||||
},
|
||||
on_confirmed: function() {
|
||||
var self = this;
|
||||
|
||||
this.view.do_execute_action(
|
||||
return this.view.do_execute_action(
|
||||
this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
|
||||
self.view.reload();
|
||||
});
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super();
|
||||
this.check_disable();
|
||||
},
|
||||
check_disable: function() {
|
||||
if (this.force_disabled || !this.view.is_interactible_record()) {
|
||||
this.$element.find("button").attr("disabled", "disabled");
|
||||
this.$element.find("button").css("color", "grey");
|
||||
} else {
|
||||
this.$element.find("button").removeAttr("disabled");
|
||||
this.$element.find("button").css("color", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetLabel',
|
||||
init: function(view, node) {
|
||||
this.element_name = 'label_' + node.attrs.name;
|
||||
|
||||
@@ -883,7 +952,6 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||
this.template = "WidgetParagraph";
|
||||
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
|
||||
} else {
|
||||
this.template = "WidgetLabel";
|
||||
this.colspan = 1;
|
||||
this.width = '1%';
|
||||
this.decrease_max_width = 1;
|
||||
@@ -902,7 +970,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||
var self = this;
|
||||
this.$element.find("label").dblclick(function() {
|
||||
var widget = self['for'] || self;
|
||||
console.log(widget.element_id , widget);
|
||||
console.log(widget.element_class , widget);
|
||||
window.w = widget;
|
||||
});
|
||||
}
|
||||
@@ -1002,12 +1070,16 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||
focus: function() {
|
||||
},
|
||||
_build_view_fields_values: function() {
|
||||
var a_dataset = this.view.dataset || {};
|
||||
var a_dataset = this.view.dataset;
|
||||
var fields_values = this.view.get_fields_values();
|
||||
var parent_values = a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {};
|
||||
fields_values.parent = parent_values;
|
||||
return fields_values;
|
||||
},
|
||||
_build_eval_context: function() {
|
||||
var a_dataset = this.view.dataset;
|
||||
return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
|
||||
},
|
||||
/**
|
||||
* Builds a new context usable for operations related to fields by merging
|
||||
* the fields'context with the action's context.
|
||||
@@ -1020,7 +1092,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||
var v_context = new openerp.web.CompoundContext();
|
||||
_.each(v_contexts, function(x) {v_context.add(x);});
|
||||
if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
|
||||
var fields_values = this._build_view_fields_values();
|
||||
var fields_values = this._build_eval_context();
|
||||
v_context.set_eval_context(fields_values);
|
||||
}
|
||||
// if there is a context on the node, overrides the model's context
|
||||
@@ -1031,7 +1103,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||
var f_domain = this.field.domain || null;
|
||||
var v_domain = this.node.attrs.domain || [];
|
||||
if (!(v_domain instanceof Array) || true) { //TODO niv: remove || true
|
||||
var fields_values = this._build_view_fields_values();
|
||||
var fields_values = this._build_eval_context();
|
||||
v_domain = new openerp.web.CompoundDomain(v_domain).set_eval_context(fields_values);
|
||||
}
|
||||
// if there is a domain on the node, overrides the model's domain
|
||||
@@ -1040,10 +1112,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||
});
|
||||
|
||||
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldChar";
|
||||
},
|
||||
template: 'FieldChar',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('input').change(this.on_ui_change);
|
||||
@@ -1052,6 +1121,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = openerp.web.format_value(value, this, '');
|
||||
this.$element.find('input').val(show_value);
|
||||
return show_value;
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -1076,10 +1146,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldEmail";
|
||||
},
|
||||
template: 'FieldEmail',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
@@ -1090,18 +1157,11 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
||||
} else {
|
||||
location.href = 'mailto:' + this.value;
|
||||
}
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val());
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldUrl";
|
||||
},
|
||||
template: 'FieldUrl',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
@@ -1252,10 +1312,7 @@ openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldText";
|
||||
},
|
||||
template: 'FieldText',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('textarea').change(this.on_ui_change);
|
||||
@@ -1288,10 +1345,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBoolean";
|
||||
},
|
||||
template: 'FieldBoolean',
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
@@ -1322,10 +1376,7 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldProgressBar";
|
||||
},
|
||||
template: 'FieldProgressBar',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('div').progressbar({
|
||||
@@ -1348,10 +1399,10 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
||||
template: 'FieldSelection',
|
||||
init: function(view, node) {
|
||||
var self = this;
|
||||
this._super(view, node);
|
||||
this.template = "FieldSelection";
|
||||
this.values = this.field.selection;
|
||||
_.each(this.values, function(v, i) {
|
||||
if (v[0] === false && v[1] === '') {
|
||||
@@ -1458,9 +1509,9 @@ openerp.web.form.dialog = function(content, options) {
|
||||
};
|
||||
|
||||
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2One',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2One";
|
||||
this.limit = 7;
|
||||
this.value = null;
|
||||
this.cm_id = _.uniqueId('m2o_cm_');
|
||||
@@ -1785,10 +1836,10 @@ var commands = {
|
||||
}
|
||||
};
|
||||
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldOne2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldOne2Many";
|
||||
this.is_started = $.Deferred();
|
||||
this.form_last_update = $.Deferred();
|
||||
this.disable_utility_classes = true;
|
||||
@@ -1819,6 +1870,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
}
|
||||
if(view.view_type === "list") {
|
||||
view.options.selectable = self.multi_selection;
|
||||
} else if (view.view_type === "form") {
|
||||
view.options.not_interactible_on_create = true;
|
||||
}
|
||||
views.push(view);
|
||||
});
|
||||
@@ -1878,6 +1931,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
switch (command[0]) {
|
||||
case commands.CREATE:
|
||||
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
|
||||
obj.defaults = {};
|
||||
self.dataset.to_create.push(obj);
|
||||
self.dataset.cache.push(_.clone(obj));
|
||||
ids.push(obj.id);
|
||||
@@ -1907,6 +1961,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
_.each(value, function(command) {
|
||||
var obj = {values: command};
|
||||
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
|
||||
obj.defaults = {};
|
||||
self.dataset.to_create.push(obj);
|
||||
self.dataset.cache.push(_.clone(obj));
|
||||
ids.push(obj.id);
|
||||
@@ -1965,9 +2020,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
},
|
||||
validate: function() {
|
||||
this.invalid = false;
|
||||
var self = this;
|
||||
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
|
||||
if (self.viewmanager.active_view === "form") {
|
||||
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()) {
|
||||
@@ -2014,7 +2068,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
||||
self.o2m.dataset.on_change();
|
||||
});
|
||||
},
|
||||
parent_view: self.o2m.view
|
||||
parent_view: self.o2m.view,
|
||||
form_view_options: {'not_interactible_on_create':true}
|
||||
}, self.o2m.build_domain(), self.o2m.build_context());
|
||||
pop.on_select_elements.add_last(function() {
|
||||
self.o2m.reload_current_view();
|
||||
@@ -2030,7 +2085,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
||||
parent_view: self.o2m.view,
|
||||
read_function: function() {
|
||||
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
|
||||
}
|
||||
},
|
||||
form_view_options: {'not_interactible_on_create':true}
|
||||
});
|
||||
pop.on_write.add(function(id, data) {
|
||||
self.o2m.dataset.write(id, data, {}, function(r) {
|
||||
@@ -2041,10 +2097,10 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2Many";
|
||||
this.list_id = _.uniqueId("many2many");
|
||||
this.is_started = $.Deferred();
|
||||
},
|
||||
@@ -2145,6 +2201,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
||||
* - alternative_form_view
|
||||
* - create_function (defaults to a naive saving behavior)
|
||||
* - parent_view
|
||||
* - form_view_options
|
||||
* - list_view_options
|
||||
*/
|
||||
select_element: function(model, options, domain, context) {
|
||||
var self = this;
|
||||
@@ -2209,7 +2267,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
||||
});
|
||||
self.view_list = new openerp.web.form.SelectCreateListView(self,
|
||||
self.dataset, false,
|
||||
{'deletable': false});
|
||||
_.extend({'deletable': false}, self.options.list_view_options || {}));
|
||||
self.view_list.popup = self;
|
||||
self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
|
||||
self.view_list.do_show();
|
||||
@@ -2245,7 +2303,7 @@ 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);
|
||||
this.view_form = new openerp.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);
|
||||
}
|
||||
@@ -2320,6 +2378,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
||||
* - auto_write (default true)
|
||||
* - read_function
|
||||
* - parent_view
|
||||
* - form_view_options
|
||||
*/
|
||||
show_element: function(model, row_id, context, options) {
|
||||
this.model = model;
|
||||
@@ -2355,7 +2414,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
||||
on_write_completed: function() {},
|
||||
setup_form_view: function() {
|
||||
var self = this;
|
||||
this.view_form = new openerp.web.FormView(this, this.dataset, false);
|
||||
this.view_form = new openerp.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);
|
||||
}
|
||||
@@ -2388,9 +2447,9 @@ openerp.web.form.FormOpenDataset = openerp.web.ReadOnlyDataSetSearch.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
||||
template: 'FieldReference',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldReference";
|
||||
this.fields_view = {
|
||||
fields: {
|
||||
selection: {
|
||||
@@ -2525,10 +2584,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBinaryFile";
|
||||
},
|
||||
template: 'FieldBinaryFile',
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = (value != null && value !== false) ? value : '';
|
||||
@@ -2556,10 +2612,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
||||
});
|
||||
|
||||
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBinaryImage";
|
||||
},
|
||||
template: 'FieldBinaryImage',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$image = this.$element.find('img.oe-binary-image');
|
||||
@@ -2634,6 +2687,93 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetNotebookReadonly = openerp.web.form.WidgetNotebook.extend({
|
||||
template: 'WidgetNotebook.readonly'
|
||||
});
|
||||
openerp.web.form.FieldReadonly = openerp.web.form.Field.extend({
|
||||
|
||||
});
|
||||
openerp.web.form.FieldCharReadonly = openerp.web.form.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
set_value: function (value) {
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = openerp.web.format_value(value, this, '');
|
||||
this.$element.find('div').text(show_value);
|
||||
return show_value;
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldURIReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
template: 'FieldURI.readonly',
|
||||
scheme: null,
|
||||
set_value: function (value) {
|
||||
var displayed = this._super.apply(this, arguments);
|
||||
this.$element.find('a')
|
||||
.attr('href', this.scheme + ':' + displayed)
|
||||
.text(displayed);
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldEmailReadonly = openerp.web.form.FieldURIReadonly.extend({
|
||||
scheme: 'mailto'
|
||||
});
|
||||
openerp.web.form.FieldUrlReadonly = openerp.web.form.FieldURIReadonly.extend({
|
||||
set_value: function (value) {
|
||||
var s = /(\w+):(\.+)/.match(value);
|
||||
if (!(s[0] === 'http' || s[0] === 'https')) { return; }
|
||||
this.scheme = s[0];
|
||||
this._super(s[1]);
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldBooleanReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
set_value: function (value) {
|
||||
this._super(value ? '\u2714' : '\u2718');
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldSelectionReadonly = openerp.web.form.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
init: function(view, node) {
|
||||
// lifted straight from r/w version
|
||||
var self = this;
|
||||
this._super(view, node);
|
||||
this.values = 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.form.FieldMany2OneReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
set_value: function (value) {
|
||||
value = value || null;
|
||||
this.invalid = false;
|
||||
var self = this;
|
||||
this.tmp_value = value;
|
||||
self.update_dom();
|
||||
self.on_value_changed();
|
||||
var real_set_value = function(rval) {
|
||||
self.$element.find('div').text(rval ? rval[1] : '');
|
||||
};
|
||||
if(typeof(value) === "number") {
|
||||
var dataset = new openerp.web.DataSetStatic(
|
||||
this, this.field.relation, self.build_context());
|
||||
dataset.name_get([value], function(data) {
|
||||
real_set_value(data[0]);
|
||||
}).fail(function() {self.tmp_value = undefined;});
|
||||
} else {
|
||||
setTimeout(function() {real_set_value(value);}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Registry of form widgets, called by :js:`openerp.web.FormView`
|
||||
*/
|
||||
@@ -2666,6 +2806,22 @@ openerp.web.form.widgets = new openerp.web.Registry({
|
||||
'binary': 'openerp.web.form.FieldBinaryFile',
|
||||
'statusbar': 'openerp.web.form.FieldStatus'
|
||||
});
|
||||
openerp.web.form.readonly = openerp.web.form.widgets.clone({
|
||||
'notebook': 'openerp.web.form.WidgetNotebookReadonly',
|
||||
'char': 'openerp.web.form.FieldCharReadonly',
|
||||
'email': 'openerp.web.form.FieldEmailReadonly',
|
||||
'url': 'openerp.web.form.FieldUrlReadonly',
|
||||
'text': 'openerp.web.form.FieldCharReadonly',
|
||||
'text_wiki' : 'openerp.web.form.FieldCharReadonly',
|
||||
'date': 'openerp.web.form.FieldCharReadonly',
|
||||
'datetime': 'openerp.web.form.FieldCharReadonly',
|
||||
'selection' : 'openerp.web.form.FieldSelectionReadonly',
|
||||
'many2one': 'openerp.web.form.FieldMany2OneReadonly',
|
||||
'boolean': 'openerp.web.form.FieldBooleanReadonly',
|
||||
'float': 'openerp.web.form.FieldCharReadonly',
|
||||
'integer': 'openerp.web.form.FieldCharReadonly',
|
||||
'float_time': 'openerp.web.form.FieldCharReadonly'
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
this.previous_colspan = null;
|
||||
this.colors = null;
|
||||
|
||||
this.columns = [];
|
||||
|
||||
@@ -75,6 +76,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
}
|
||||
self.compute_aggregates();
|
||||
});
|
||||
|
||||
},
|
||||
/**
|
||||
* Retrieves the view's number of records per page (|| section)
|
||||
@@ -131,6 +133,31 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
this.$element.addClass('oe-listview');
|
||||
return this.reload_view(null, null, true);
|
||||
},
|
||||
/**
|
||||
* Returns the color for the provided record in the current view (from the
|
||||
* ``@colors`` attribute)
|
||||
*
|
||||
* @param {Record} record record for the current row
|
||||
* @returns {String} CSS color declaration
|
||||
*/
|
||||
color_for: function (record) {
|
||||
if (!this.colors) { return ''; }
|
||||
var context = _.extend({}, record.attributes, {
|
||||
uid: this.session.uid,
|
||||
current_date: new Date().toString('yyyy-MM-dd')
|
||||
// TODO: time, datetime, relativedelta
|
||||
});
|
||||
for(var i=0, len=this.colors.length; i<len; ++i) {
|
||||
var pair = this.colors[i],
|
||||
color = pair[0],
|
||||
expression = pair[1];
|
||||
if (py.evaluate(expression, context)) {
|
||||
return 'color: ' + color + ';';
|
||||
}
|
||||
// TODO: handle evaluation errors
|
||||
}
|
||||
return '';
|
||||
},
|
||||
/**
|
||||
* Called after loading the list view's description, sets up such things
|
||||
* as the view table's columns, renders the table itself and hooks up the
|
||||
@@ -159,6 +186,17 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
this.fields_view = data;
|
||||
this.name = "" + this.fields_view.arch.attrs.string;
|
||||
|
||||
if (this.fields_view.arch.attrs.colors) {
|
||||
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
|
||||
.compact()
|
||||
.map(function(color_pair) {
|
||||
var pair = color_pair.split(':'),
|
||||
color = pair[0],
|
||||
expr = pair[1];
|
||||
return [color, py.parse(py.tokenize(expr)), expr];
|
||||
}).value();
|
||||
}
|
||||
|
||||
this.setup_columns(this.fields_view.fields, grouped);
|
||||
|
||||
this.$element.html(QWeb.render("ListView", this));
|
||||
@@ -1355,10 +1393,10 @@ var Record = openerp.web.Class.extend(/** @lends Record# */{
|
||||
* @returns {Object} record displayable in a form view
|
||||
*/
|
||||
toForm: function () {
|
||||
var form_data = {};
|
||||
_(this.attributes).each(function (value, key) {
|
||||
form_data[key] = {value: value};
|
||||
});
|
||||
var form_data = {}, attrs = this.attributes;
|
||||
for(var k in attrs) {
|
||||
form_data[k] = {value: attrs[k]};
|
||||
}
|
||||
|
||||
return {data: form_data};
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ db.web.ActionManager = db.web.Widget.extend({
|
||||
console.log("Action manager can't handle action of type " + action.type, action);
|
||||
return;
|
||||
}
|
||||
this[type](action, on_close);
|
||||
return this[type](action, on_close);
|
||||
},
|
||||
ir_actions_act_window: function (action, on_close) {
|
||||
if (action.target === 'new') {
|
||||
@@ -163,7 +163,7 @@ db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{
|
||||
*/
|
||||
init: function(parent, dataset, views) {
|
||||
this._super(parent);
|
||||
this.model = dataset.model;
|
||||
this.model = dataset ? dataset.model : undefined;
|
||||
this.dataset = dataset;
|
||||
this.searchview = null;
|
||||
this.active_view = null;
|
||||
@@ -184,7 +184,6 @@ db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{
|
||||
start: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
this.dataset.start();
|
||||
this.$element.find('.oe_vm_switch button').click(function() {
|
||||
self.on_mode_switch($(this).data('view-type'));
|
||||
});
|
||||
@@ -332,6 +331,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
|
||||
// dataset initialization will take the session from ``this``, so if we
|
||||
// do not have it yet (and we don't, because we've not called our own
|
||||
// ``_super()``) rpc requests will blow up.
|
||||
this._super(parent, null, action.views);
|
||||
this.session = parent.session;
|
||||
this.action = action;
|
||||
var dataset = new db.web.DataSetSearch(this, action.res_model, action.context, action.domain);
|
||||
@@ -339,7 +339,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
|
||||
dataset.ids.push(action.res_id);
|
||||
dataset.index = 0;
|
||||
}
|
||||
this._super(parent, dataset, action.views);
|
||||
this.dataset = dataset;
|
||||
this.flags = this.action.flags || {};
|
||||
if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
|
||||
// Not elegant but allows to avoid form chrome (pager, save/new
|
||||
@@ -470,7 +470,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
|
||||
* Intercept do_action resolution from children views
|
||||
*/
|
||||
on_action_executed: function () {
|
||||
new db.web.DataSet(this, 'res.log')
|
||||
return new db.web.DataSet(this, 'res.log')
|
||||
.call('get', [], this.do_display_log);
|
||||
},
|
||||
/**
|
||||
@@ -770,28 +770,31 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
|
||||
var self = this;
|
||||
var result_handler = function () {
|
||||
if (on_closed) { on_closed.apply(null, arguments); }
|
||||
self.widget_parent.on_action_executed.apply(null, arguments);
|
||||
return self.widget_parent.on_action_executed.apply(null, arguments);
|
||||
};
|
||||
var handler = function (r) {
|
||||
var action = r.result;
|
||||
if (action && action.constructor == Object) {
|
||||
action.context = action.context || {};
|
||||
_.extend(action.context, {
|
||||
active_id: record_id || false,
|
||||
active_ids: [record_id || false],
|
||||
active_model: dataset.model
|
||||
return self.rpc('/web/session/eval_domain_and_context', {
|
||||
contexts: [dataset.get_context(), action.context || {}, {
|
||||
active_id: record_id || false,
|
||||
active_ids: [record_id || false],
|
||||
active_model: dataset.model
|
||||
}],
|
||||
domains: []
|
||||
}).pipe(function (results) {
|
||||
action.context = results.context
|
||||
return self.do_action(action, result_handler);
|
||||
});
|
||||
action.context = new db.web.CompoundContext(dataset.get_context(), action.context);
|
||||
self.do_action(action, result_handler);
|
||||
} else {
|
||||
result_handler();
|
||||
return result_handler();
|
||||
}
|
||||
};
|
||||
|
||||
var context = new db.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
||||
|
||||
if (action_data.special) {
|
||||
handler({result: {"type":"ir.actions.act_window_close"}});
|
||||
return handler({result: {"type":"ir.actions.act_window_close"}});
|
||||
} else if (action_data.type=="object") {
|
||||
return dataset.call_button(action_data.name, [[record_id], context], handler);
|
||||
} else if (action_data.type=="action") {
|
||||
|
||||
@@ -242,7 +242,23 @@
|
||||
</table>
|
||||
</form>
|
||||
</t>
|
||||
|
||||
<t t-name="Login_dblist">
|
||||
<select name="db">
|
||||
<t t-foreach="db_list" t-as="db">
|
||||
<t t-if="selected_db === db">
|
||||
<option t-att-value="db" selected="true">
|
||||
<t t-esc="db"/></option>
|
||||
</t>
|
||||
<t t-if="selected_db !== db">
|
||||
<option t-att-value="db"><t t-esc="db"/></option>
|
||||
</t>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
|
||||
<t t-name="Login">
|
||||
<div>
|
||||
<form class="oe_forms">
|
||||
<fieldset>
|
||||
<legend style="">
|
||||
@@ -253,33 +269,18 @@
|
||||
<tr>
|
||||
<td><label for="db">Database:</label></td>
|
||||
<td>
|
||||
<t t-if="!db_list">
|
||||
<input type="text" name="db" t-att-value="selected_db || ''" autofocus="true"/>
|
||||
</t>
|
||||
<t t-if="db_list">
|
||||
<select name="db">
|
||||
<t t-foreach="db_list" t-as="db">
|
||||
<t t-if="selected_db === db">
|
||||
<option t-att-value="db" selected="true">
|
||||
<t t-esc="db"/></option>
|
||||
</t>
|
||||
<t t-if="selected_db !== db">
|
||||
<option t-att-value="db"><t t-esc="db"/></option>
|
||||
</t>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
<input type="text" name="db" t-att-value="widget.selected_db || ''" autofocus="true"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="login">User:</label></td>
|
||||
<td><input type="text" name="login"
|
||||
t-att-value="selected_login || ''" autofocus="true"/></td>
|
||||
t-att-value="widget.selected_login || ''" autofocus="true"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="password">Password:</label></td>
|
||||
<td><input type="password" name="password"
|
||||
t-att-value="selected_password || ''"/></td>
|
||||
t-att-value="widget.selected_password || ''"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
@@ -330,6 +331,7 @@
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Header">
|
||||
<div>
|
||||
@@ -614,7 +616,8 @@
|
||||
</t>
|
||||
</t>
|
||||
<tr t-name="ListView.row" t-att-class="row_parity"
|
||||
t-att-data-id="record.get('id')">
|
||||
t-att-data-id="record.get('id')"
|
||||
t-att-style="view.color_for(record)">
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
@@ -639,7 +642,7 @@
|
||||
<t t-raw="frame.render()"/>
|
||||
</t>
|
||||
<t t-name="FormView">
|
||||
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
|
||||
<div class="oe_form_header">
|
||||
<div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
|
||||
<!--<button type="button" class="oe_form_button_save">
|
||||
<span class="oe_form_on_update">Save</span>
|
||||
@@ -652,6 +655,7 @@
|
||||
<!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
|
||||
<button type="button" class="oe_form_button_new">New</button>
|
||||
<button type="button" class="oe_form_button_duplicate oe_form_on_update">Duplicate</button>
|
||||
<button type="button" class="oe_form_button_toggle">Readonly/Editable</button>
|
||||
</div>
|
||||
<div class="oe_form_pager" t-if="view.options.pager !== false">
|
||||
<button type="button" data-pager-action="first">First</button>
|
||||
@@ -711,8 +715,7 @@
|
||||
t-att-width="td.width"
|
||||
t-att-nowrap="td.nowrap or td.is_field_m2o? 'true' : undefined"
|
||||
t-att-valign="td.table ? 'top' : undefined"
|
||||
t-att-id="td.element_id"
|
||||
t-attf-class="oe_form_frame_cell #{td.classname}"
|
||||
t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}"
|
||||
>
|
||||
<t t-raw="td.render()"/>
|
||||
</td>
|
||||
@@ -722,8 +725,8 @@
|
||||
</t>
|
||||
<t t-name="WidgetNotebook">
|
||||
<ul>
|
||||
<li t-foreach="widget.pages" t-as="page" t-att-id="page.element_tab_id">
|
||||
<a t-att-href="'#' + page.element_id">
|
||||
<li t-foreach="widget.pages" t-as="page">
|
||||
<a href="#">
|
||||
<t t-esc="page.string"/>
|
||||
</a>
|
||||
</li>
|
||||
@@ -733,17 +736,23 @@
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetNotebookPage">
|
||||
<div t-att-id="widget.element_id">
|
||||
<div>
|
||||
<t t-call="WidgetFrame"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="WidgetNotebook.readonly">
|
||||
<t t-foreach="widget.pages" t-as="page">
|
||||
<h3><t t-esc="page.string"/></h3>
|
||||
<t t-raw="page.render()"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetSeparator">
|
||||
<div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
|
||||
<t t-esc="widget.string"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="WidgetLabel">
|
||||
<label t-att-for="widget.element_id + '_field'"
|
||||
<label t-att-for="widget.element_id"
|
||||
t-att-class="'oe_label' + (widget.help ? '_help' : '')"
|
||||
t-att-title="widget.help">
|
||||
<t t-esc="widget.string"/>
|
||||
@@ -757,12 +766,22 @@
|
||||
<t t-name="FieldChar">
|
||||
<input type="text" size="1"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-id="widget.element_id"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
/>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/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} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%">
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldURI.readonly">
|
||||
<a href="#">#</a>
|
||||
</t>
|
||||
<t t-name="FieldEmail">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tr>
|
||||
@@ -794,8 +813,8 @@
|
||||
<t t-name="FieldText">
|
||||
<textarea rows="6"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-id="widget.element_id"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
></textarea>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
@@ -815,7 +834,7 @@
|
||||
<select
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
style="width: 100%">
|
||||
<t t-foreach="widget.values" t-as="option">
|
||||
<option><t t-esc="option[1]"/></option>
|
||||
@@ -823,9 +842,9 @@
|
||||
</select>
|
||||
</t>
|
||||
<t t-name="FieldMany2One">
|
||||
<div t-att-id="widget.element_id" class="oe-m2o">
|
||||
<input t-att-id="widget.element_id + '_input'" type="text" size="1" style="width: 100%;"/>
|
||||
<span class="oe-m2o-drop-down-button" t-att-id="widget.element_id + '_drop_down'">
|
||||
<div t-att-class="widget.element_class" class="oe-m2o">
|
||||
<input type="text" size="1" style="width: 100%;"/>
|
||||
<span class="oe-m2o-drop-down-button">
|
||||
<img src="/web/static/src/img/down-arrow.png" /></span>
|
||||
<span class="oe-m2o-cm-button" t-att-id="widget.name + '_open'">
|
||||
<img src="/web/static/src/img/icons/gtk-index.png"/></span>
|
||||
@@ -848,8 +867,6 @@
|
||||
</ul>
|
||||
</t>
|
||||
<t t-name="FieldOne2Many">
|
||||
<div t-att-id="widget.element_id">
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldMany2Many">
|
||||
<div t-att-id="widget.list_id"></div>
|
||||
@@ -857,10 +874,10 @@
|
||||
<t t-name="FieldReference">
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
|
||||
<tr>
|
||||
<td t-att-id="widget.selection.element_id" class="oe_form_frame_cell oe_form_selection">
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_selection #{widget.selection.element_class}">
|
||||
<t t-raw="widget.selection.render()"/>
|
||||
</td>
|
||||
<td t-att-id="widget.m2o.element_id" class="oe_form_frame_cell oe_form_many2one" nowrap="true">
|
||||
<td class="oe_form_frame_cell oe_form_many2one #{widget.selection.element_class}" nowrap="true">
|
||||
<t t-raw="widget.m2o.render()"/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -870,7 +887,7 @@
|
||||
<input type="checkbox"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"/>
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"/>
|
||||
</t>
|
||||
<t t-name="FieldProgressBar">
|
||||
<div t-opentag="true" class="oe-progressbar">
|
||||
@@ -885,7 +902,7 @@
|
||||
t-att-border="widget.readonly ? 0 : 1"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-name="widget.name"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
|
||||
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
|
||||
/>
|
||||
@@ -933,7 +950,7 @@
|
||||
<input type="text" size="1"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type" style="width: 100%"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}" style="width: 100%"
|
||||
/>
|
||||
</td>
|
||||
<td class="oe-binary" nowrap="true">
|
||||
@@ -978,7 +995,7 @@
|
||||
</t>
|
||||
<t t-name="WidgetButton">
|
||||
<button type="button"
|
||||
t-att-id="widget.element_id + '_button'"
|
||||
t-attf-class="#{widget.element_class}"
|
||||
t-att-title="widget.help"
|
||||
style="width: 100%" class="button">
|
||||
<img t-if="widget.node.attrs.icon" t-att-src="'/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
|
||||
|
||||
@@ -35,6 +35,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
|
||||
},
|
||||
stop: function() {
|
||||
scheduler.clearAll();
|
||||
this._super();
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.fields_view = data;
|
||||
|
||||
@@ -301,7 +301,7 @@ openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({
|
||||
.then(this.on_records_loaded);
|
||||
},
|
||||
on_records_loaded: function (read_response, progress_response) {
|
||||
var records = read_response[0].records,
|
||||
var records = read_response,
|
||||
progress = progress_response[0];
|
||||
|
||||
var grouped_todos = _(records).chain()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "2.0",
|
||||
"depends": ['web'],
|
||||
"js": [
|
||||
"static/lib/dhtmlxGraph/codebase/dhtmlxchart.js",
|
||||
"static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js",
|
||||
"static/src/js/graph.js"],
|
||||
"css": ["static/lib/dhtmlxGraph/codebase/dhtmlxchart.css"],
|
||||
"active": True
|
||||
|
||||
@@ -19,11 +19,14 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
|
||||
init: function(parent, dataset, view_id) {
|
||||
this._super(parent);
|
||||
this.view_manager = parent;
|
||||
this.dataset = dataset;
|
||||
this.dataset_index = 0;
|
||||
this.model = this.dataset.model;
|
||||
this.view_id = view_id;
|
||||
|
||||
this.first_field = null;
|
||||
this.abscissa = null;
|
||||
this.ordinate = null;
|
||||
this.columns = [];
|
||||
this.group_field = null;
|
||||
},
|
||||
do_show: function () {
|
||||
// TODO: re-trigger search
|
||||
@@ -36,355 +39,262 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
var self = this;
|
||||
this._super();
|
||||
return $.when(
|
||||
new openerp.web.DataSet(this, this.model).call('fields_get', []),
|
||||
this.dataset.call('fields_get', []),
|
||||
this.rpc('/web/view/load', {
|
||||
model: this.model,
|
||||
model: this.dataset.model,
|
||||
view_id: this.view_id,
|
||||
view_type: 'graph'
|
||||
})).then(function (fields_result, view_result) {
|
||||
self.on_loaded({
|
||||
all_fields: fields_result[0],
|
||||
fields_view: view_result[0]
|
||||
});
|
||||
self.fields = fields_result[0];
|
||||
self.fields_view = view_result[0];
|
||||
self.on_loaded();
|
||||
});
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.all_fields = data.all_fields;
|
||||
this.fields_view = data.fields_view;
|
||||
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
|
||||
this.view_id = this.fields_view.view_id;
|
||||
/**
|
||||
* Returns all object fields involved in the graph view
|
||||
*/
|
||||
list_fields: function () {
|
||||
var fs = [this.abscissa];
|
||||
fs.push.apply(fs, _(this.columns).pluck('name'));
|
||||
if (this.group_field) {
|
||||
fs.push(this.group_field);
|
||||
}
|
||||
return fs;
|
||||
},
|
||||
on_loaded: function() {
|
||||
this.chart = this.fields_view.arch.attrs.type || 'pie';
|
||||
this.fields = this.fields_view.fields;
|
||||
this.chart_info_fields = [];
|
||||
this.operator_field = '';
|
||||
this.operator_field_one = '';
|
||||
this.operator = [];
|
||||
this.group_field = [];
|
||||
this.orientation = this.fields_view.arch.attrs.orientation || '';
|
||||
this.orientation = this.fields_view.arch.attrs.orientation || 'vertical';
|
||||
|
||||
_.each(this.fields_view.arch.children, function (field) {
|
||||
if (field.attrs.operator) {
|
||||
this.operator.push(field.attrs.name);
|
||||
}
|
||||
else if (field.attrs.group) {
|
||||
this.group_field.push(field.attrs.name);
|
||||
}
|
||||
else {
|
||||
this.chart_info_fields.push(field.attrs.name);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.operator_field = this.operator[0];
|
||||
if(this.operator.length > 1){
|
||||
this.operator_field_one = this.operator[1];
|
||||
}
|
||||
if(this.operator == ''){
|
||||
this.operator_field = this.chart_info_fields[1];
|
||||
}
|
||||
this.chart_info = this.chart_info_fields[0];
|
||||
this.x_title = this.fields[this.chart_info_fields[0]]['string'];
|
||||
this.y_title = this.fields[this.operator_field]['string'];
|
||||
this.load_chart();
|
||||
},
|
||||
|
||||
load_chart: function(data) {
|
||||
var self = this;
|
||||
var domain = false;
|
||||
if(data){
|
||||
this.x_title = this.all_fields[this.chart_info_fields]['string'];
|
||||
this.y_title = this.all_fields[this.operator_field]['string'];
|
||||
self.schedule_chart(data);
|
||||
}else{
|
||||
if(! _.isEmpty(this.view_manager.dataset.domain)){
|
||||
domain = this.view_manager.dataset.domain;
|
||||
}else if(! _.isEmpty(this.view_manager.action.domain)){
|
||||
domain = this.view_manager.action.domain;
|
||||
}
|
||||
this.dataset.domain = domain;
|
||||
this.dataset.context = this.view_manager.dataset.context;
|
||||
this.dataset.read_slice(_(this.fields).keys(),{}, function(res) {
|
||||
self.schedule_chart(res);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
schedule_chart: function(results) {
|
||||
this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'element_id': this.element_id}));
|
||||
|
||||
_.each(results, function (result) {
|
||||
_.each(result, function (field_value, field_name) {
|
||||
if (typeof field_value == 'object') {
|
||||
result[field_name] = field_value[field_value.length - 1];
|
||||
}
|
||||
if (typeof field_value == 'string') {
|
||||
var choices = this.all_fields[field_name]['selection'];
|
||||
_.each(choices, function (choice) {
|
||||
if (field_value == choice[0]) {
|
||||
result[field_name] = choice;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
var graph_data = {};
|
||||
_.each(results, function (result) {
|
||||
var group_key = [];
|
||||
if(this.group_field.length){
|
||||
_.each(this.group_field, function (res) {
|
||||
result[res] = (typeof result[res] == 'object') ? result[res][1] : result[res];
|
||||
group_key.push(result[res]);
|
||||
});
|
||||
}else{
|
||||
group_key.push(result[this.group_field]);
|
||||
}
|
||||
var column_key = result[this.chart_info_fields] + "_" + group_key;
|
||||
var column_descriptor = {};
|
||||
if (graph_data[column_key] == undefined) {
|
||||
column_descriptor[this.operator_field] = result[this.operator_field];
|
||||
if (this.operator.length > 1) {
|
||||
column_descriptor[this.operator_field_one] = result[this.operator_field_one];
|
||||
}
|
||||
column_descriptor[this.chart_info_fields] = result[this.chart_info_fields];
|
||||
if(this.group_field.length){
|
||||
_.each(this.group_field, function (res) {
|
||||
column_descriptor[res] = (typeof result[res] == 'object') ? result[res][1] : result[res];
|
||||
});
|
||||
}
|
||||
var attrs = field.attrs;
|
||||
if (attrs.group) {
|
||||
this.group_field = attrs.name;
|
||||
} else if(!this.abscissa) {
|
||||
this.first_field = this.abscissa = attrs.name;
|
||||
} else {
|
||||
column_descriptor = graph_data[column_key];
|
||||
column_descriptor[this.operator_field] += result[this.operator_field];
|
||||
if (this.operator.length > 1) {
|
||||
column_descriptor[this.operator_field_one] += result[this.operator_field_one];
|
||||
}
|
||||
this.columns.push({
|
||||
name: attrs.name,
|
||||
operator: attrs.operator || '+'
|
||||
});
|
||||
}
|
||||
graph_data[column_key] = column_descriptor;
|
||||
}, this);
|
||||
this.ordinate = this.columns[0].name;
|
||||
|
||||
this.dataset.read_slice(
|
||||
this.list_fields(), {}, $.proxy(this, 'schedule_chart'));
|
||||
},
|
||||
schedule_chart: function(results) {
|
||||
var self = this;
|
||||
this.$element.html(QWeb.render("GraphView", {
|
||||
"fields_view": this.fields_view,
|
||||
"chart": this.chart,
|
||||
'element_id': this.element_id
|
||||
}));
|
||||
|
||||
var fields = _(this.columns).pluck('name').concat([this.abscissa]);
|
||||
if (this.group_field) { fields.push(this.group_field); }
|
||||
// transform search result into usable records (convert from OpenERP
|
||||
// value shapes to usable atomic types
|
||||
var records = _(results).map(function (result) {
|
||||
var point = {};
|
||||
_(result).each(function (value, field) {
|
||||
if (!_(fields).contains(field)) { return; }
|
||||
if (value === false) { point[field] = false; return; }
|
||||
switch (self.fields[field].type) {
|
||||
case 'selection':
|
||||
point[field] = _(self.fields[field].selection).detect(function (choice) {
|
||||
return choice[0] === value;
|
||||
})[1];
|
||||
break;
|
||||
case 'many2one':
|
||||
point[field] = value[1];
|
||||
break;
|
||||
case 'integer': case 'float': case 'char':
|
||||
case 'date': case 'datetime':
|
||||
point[field] = value;
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
"Unknown field type " + self.fields[field].type
|
||||
+ "for field " + field + " (" + value + ")");
|
||||
}
|
||||
});
|
||||
return point;
|
||||
});
|
||||
// aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
|
||||
// leave split on group field => max m*n records where m is the # of
|
||||
// values for the abscissa and n is the # of values for the group field
|
||||
var graph_data = [];
|
||||
_(records).each(function (record) {
|
||||
var abscissa = record[self.abscissa],
|
||||
group = record[self.group_field];
|
||||
var r = _(graph_data).detect(function (potential) {
|
||||
return potential[self.abscissa] === abscissa
|
||||
&& (!self.group_field
|
||||
|| potential[self.group_field] === group);
|
||||
});
|
||||
var datapoint = r || {};
|
||||
|
||||
datapoint[self.abscissa] = abscissa;
|
||||
if (self.group_field) { datapoint[self.group_field] = group; }
|
||||
_(self.columns).each(function (column) {
|
||||
var val = record[column.name],
|
||||
aggregate = datapoint[column.name];
|
||||
switch(column.operator) {
|
||||
case '+':
|
||||
datapoint[column.name] = (aggregate || 0) + val;
|
||||
return;
|
||||
case '*':
|
||||
datapoint[column.name] = (aggregate || 1) * val;
|
||||
return;
|
||||
case 'min':
|
||||
datapoint[column.name] = (aggregate || Infinity) > val
|
||||
? val
|
||||
: aggregate;
|
||||
return;
|
||||
case 'max':
|
||||
datapoint[column.name] = (aggregate || -Infinity) < val
|
||||
? val
|
||||
: aggregate;
|
||||
}
|
||||
});
|
||||
|
||||
if (!r) { graph_data.push(datapoint); }
|
||||
});
|
||||
graph_data = _(graph_data).sortBy(function (point) {
|
||||
return point[self.abscissa] + '[[--]]' + point[self.group_field];
|
||||
});
|
||||
|
||||
if (this.chart == 'bar') {
|
||||
return this.schedule_bar(_.values(graph_data));
|
||||
return this.schedule_bar(graph_data);
|
||||
} else if (this.chart == "pie") {
|
||||
return this.schedule_pie(_.values(graph_data));
|
||||
return this.schedule_pie(graph_data);
|
||||
}
|
||||
},
|
||||
|
||||
schedule_bar: function(results) {
|
||||
var self = this;
|
||||
var view_chart = '';
|
||||
var group_list = [];
|
||||
var legend_list = [];
|
||||
var newkey = '', newkey_one;
|
||||
var string_legend = '';
|
||||
|
||||
if((self.group_field.length) && (this.operator.length <= 1)){
|
||||
view_chart = self.orientation == 'horizontal'? 'stackedBarH' : 'stackedBar';
|
||||
}else{
|
||||
view_chart = self.orientation == 'horizontal'? 'barH' : 'bar';
|
||||
}
|
||||
|
||||
_.each(results, function (result) {
|
||||
if ((self.group_field.length) && (this.operator.length <= 1)) {
|
||||
var legend_key = '';
|
||||
_.each(self.group_field, function (res) {
|
||||
result[res] = (typeof result[res] == 'object') ? result[res][1] : result[res];
|
||||
legend_key += result[res];
|
||||
});
|
||||
newkey = legend_key.replace(/\s+/g,'_').replace(/[^a-zA-Z 0-9]+/g,'_');
|
||||
string_legend = legend_key;
|
||||
} else {
|
||||
newkey = string_legend = "val";
|
||||
}
|
||||
|
||||
if (_.contains(group_list, newkey) && _.contains(legend_list, string_legend)) {
|
||||
return;
|
||||
}
|
||||
group_list.push(newkey);
|
||||
legend_list.push(string_legend);
|
||||
|
||||
if (this.operator.length > 1) {
|
||||
newkey_one = "val1";
|
||||
group_list.push(newkey_one);
|
||||
legend_list.push(newkey_one);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (group_list.length <=1){
|
||||
group_list = [];
|
||||
legend_list = [];
|
||||
newkey = string_legend = "val";
|
||||
group_list.push(newkey);
|
||||
legend_list.push(string_legend);
|
||||
}
|
||||
|
||||
var abscissa_data = {};
|
||||
_.each(results, function (result) {
|
||||
var label = result[self.chart_info_fields],
|
||||
section = {};
|
||||
if ((self.group_field.length) && (group_list.length > 1) && (self.operator.length <= 1)){
|
||||
var legend_key_two = '';
|
||||
_.each(self.group_field, function (res) {
|
||||
result[res] = (typeof result[res] == 'object') ? result[res][1] : result[res];
|
||||
legend_key_two += result[res];
|
||||
});
|
||||
newkey = legend_key_two.replace(/\s+/g,'_').replace(/[^a-zA-Z 0-9]+/g,'_');
|
||||
}else{
|
||||
newkey = "val";
|
||||
}
|
||||
if (abscissa_data[label] == undefined){
|
||||
section[self.chart_info_fields] = label;
|
||||
_.each(group_list, function (group) {
|
||||
section[group] = 0;
|
||||
});
|
||||
} else {
|
||||
section = abscissa_data[label];
|
||||
}
|
||||
section[newkey] = result[self.operator_field];
|
||||
if (self.operator.length > 1){
|
||||
section[newkey_one] = result[self.operator_field_one];
|
||||
}
|
||||
abscissa_data[label] = section;
|
||||
});
|
||||
|
||||
//for legend color
|
||||
var grp_color = _.map(legend_list, function (group_legend, index) {
|
||||
var legend = {color: COLOR_PALETTE[index]};
|
||||
if (group_legend == "val"){
|
||||
legend['text'] = self.fields[self.operator_field]['string']
|
||||
}else if(group_legend == "val1"){
|
||||
legend['text'] = self.fields[self.operator_field_one]['string']
|
||||
}else{
|
||||
legend['text'] = group_legend;
|
||||
}
|
||||
return legend;
|
||||
});
|
||||
|
||||
//for axis's value and title
|
||||
var max,min,step;
|
||||
var maximum,minimum;
|
||||
if(_.isEmpty(abscissa_data)){
|
||||
max = 9;
|
||||
min = 0;
|
||||
step=1;
|
||||
}else{
|
||||
var max_min = [];
|
||||
_.each(abscissa_data, function (abscissa_datas) {
|
||||
_.each(group_list, function(res){
|
||||
max_min.push(abscissa_datas[res]);
|
||||
});
|
||||
});
|
||||
maximum = Math.max.apply(Math,max_min);
|
||||
minimum = Math.min.apply(Math,max_min);
|
||||
if (maximum == minimum){
|
||||
if (maximum == 0){
|
||||
max = 9;
|
||||
min = 0;
|
||||
step=1;
|
||||
}else if(maximum > 0){
|
||||
max = maximum + (10 - maximum % 10);
|
||||
min = 0;
|
||||
step = Math.round(max/10);
|
||||
}else{
|
||||
max = 0;
|
||||
min = minimum - (10 + minimum % 10);
|
||||
step = Math.round(Math.abs(min)/10);
|
||||
var group_list, view_chart;
|
||||
if (!this.group_field) {
|
||||
view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
|
||||
group_list = _(this.columns).map(function (column, index) {
|
||||
return {
|
||||
group: column.name,
|
||||
text: self.fields[column.name].string,
|
||||
color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// dhtmlx handles clustered bar charts (> 1 column per abscissa
|
||||
// value) and stacked bar charts (basically the same but with the
|
||||
// columns on top of one another instead of side by side), but it
|
||||
// does not handle clustered stacked bar charts
|
||||
if (this.columns.length > 1) {
|
||||
this.$element.text(
|
||||
'OpenERP Web does not support combining grouping and '
|
||||
+ 'multiple columns in graph at this time.');
|
||||
throw new Error(
|
||||
'dhtmlx can not handle columns counts of that magnitude');
|
||||
}
|
||||
// transform series for clustered charts into series for stacked
|
||||
// charts
|
||||
view_chart = (this.orientation === 'horizontal')
|
||||
? 'stackedBarH' : 'stackedBar';
|
||||
group_list = _(results).chain()
|
||||
.pluck(this.group_field)
|
||||
.uniq()
|
||||
.map(function (value, index) {
|
||||
return {
|
||||
group: self.ordinate + '_' +
|
||||
value.toLowerCase().replace(/\s/g, '_'),
|
||||
text: value,
|
||||
color: COLOR_PALETTE[index % COLOR_PALETTE.length]
|
||||
};
|
||||
}).value();
|
||||
|
||||
results = _(results).chain()
|
||||
.groupBy(function (record) { return record[self.abscissa]; })
|
||||
.map(function (records) {
|
||||
var r = {};
|
||||
// second argument is coerced to a str, no good for boolean
|
||||
r[self.abscissa] = records[0][self.abscissa];
|
||||
_(records).each(function (record) {
|
||||
var key = _.sprintf('%s_%s',
|
||||
self.ordinate,
|
||||
record[self.group_field].toLowerCase().replace(/\s/g, '_'));
|
||||
r[key] = record[self.ordinate];
|
||||
});
|
||||
return r;
|
||||
})
|
||||
.value();
|
||||
}
|
||||
|
||||
var abscissa_description = {
|
||||
template: self.chart_info_fields,
|
||||
title: "<b>"+self.x_title+"</b>"
|
||||
title: "<b>" + this.fields[this.abscissa].string + "</b>",
|
||||
template: function (obj) {
|
||||
return obj[self.abscissa] || 'Undefined';
|
||||
}
|
||||
};
|
||||
|
||||
var ordinate_description = {
|
||||
lines: true,
|
||||
title: "<b>"+self.y_title+"</b>",
|
||||
start: min,
|
||||
step: step,
|
||||
end: max
|
||||
title: "<b>" + this.fields[this.ordinate].string + "</b>"
|
||||
};
|
||||
|
||||
var x_axis, y_axis, tooltip;
|
||||
if (self.orientation == 'horizontal'){
|
||||
x_axis = ordinate_description;
|
||||
y_axis = abscissa_description;
|
||||
}else{
|
||||
x_axis = abscissa_description;
|
||||
y_axis = ordinate_description;
|
||||
var x_axis, y_axis;
|
||||
if (self.orientation == 'horizontal') {
|
||||
x_axis = ordinate_description;
|
||||
y_axis = abscissa_description;
|
||||
} else {
|
||||
x_axis = abscissa_description;
|
||||
y_axis = ordinate_description;
|
||||
}
|
||||
tooltip = self.chart_info_fields;
|
||||
|
||||
|
||||
var bar_chart = new dhtmlXChart({
|
||||
view: view_chart,
|
||||
container: self.element_id+"-barchart",
|
||||
value:"#"+group_list[0]+"#",
|
||||
container: this.element_id+"-barchart",
|
||||
value:"#"+group_list[0].group+"#",
|
||||
gradient: "3d",
|
||||
border: false,
|
||||
width: 1024,
|
||||
tooltip:{
|
||||
template:"#"+tooltip+"#"+","+grp_color[0]['text']+"="+"#"+group_list[0]+"#"
|
||||
template: _.sprintf("#%s#, %s=#%s#",
|
||||
this.abscissa, group_list[0].text, group_list[0].group)
|
||||
},
|
||||
radius: 0,
|
||||
color:grp_color[0]['color'],
|
||||
color:group_list[0].color,
|
||||
origin:0,
|
||||
xAxis:{
|
||||
template:function(obj){
|
||||
if(x_axis['template']){
|
||||
var val = obj[x_axis['template']];
|
||||
val = (typeof val == 'object')?val[1]:(!val?'Undefined':val);
|
||||
if(val.length > 12){
|
||||
val = val.substring(0,12);
|
||||
}
|
||||
return val;
|
||||
}else{
|
||||
return obj;
|
||||
}
|
||||
},
|
||||
title:x_axis['title'],
|
||||
lines:x_axis['lines']
|
||||
},
|
||||
yAxis:{
|
||||
template:function(obj){
|
||||
if(y_axis['template']){
|
||||
var vals = obj[y_axis['template']];
|
||||
vals = (typeof vals == 'object')?vals[1]:(!vals?'Undefined':vals);
|
||||
if(vals.length > 12){
|
||||
vals = vals.substring(0,12);
|
||||
}
|
||||
return vals;
|
||||
}else{
|
||||
return obj;
|
||||
}
|
||||
},
|
||||
title:y_axis['title'],
|
||||
lines: y_axis['lines'],
|
||||
start:y_axis['start'],
|
||||
step:y_axis['step'],
|
||||
end:y_axis['end']
|
||||
},
|
||||
xAxis: x_axis,
|
||||
yAxis: y_axis,
|
||||
padding: {
|
||||
left: 75
|
||||
},
|
||||
legend: {
|
||||
values: grp_color,
|
||||
values: group_list,
|
||||
align:"left",
|
||||
valign:"top",
|
||||
layout: "x",
|
||||
marker:{
|
||||
marker: {
|
||||
type:"round",
|
||||
width:12
|
||||
}
|
||||
}
|
||||
});
|
||||
for (var m = 1; m<group_list.length;m++){
|
||||
var column = group_list[m];
|
||||
if (column.group === this.group_field) { continue; }
|
||||
bar_chart.addSeries({
|
||||
value: "#"+group_list[m]+"#",
|
||||
value: "#"+column.group+"#",
|
||||
tooltip:{
|
||||
template:"#"+tooltip+"#"+","+grp_color[m]['text']+"="+"#"+group_list[m]+"#"
|
||||
template: _.sprintf("#%s#, %s=#%s#",
|
||||
this.abscissa, column.text, column.group)
|
||||
},
|
||||
color: grp_color[m]['color']
|
||||
color: column.color
|
||||
});
|
||||
}
|
||||
bar_chart.parse(_.values(abscissa_data), "json");
|
||||
jQuery("#"+self.element_id+"-barchart").height(jQuery("#"+self.element_id+"-barchart").height()+50);
|
||||
bar_chart.parse(results, "json");
|
||||
jQuery("#"+this.element_id+"-barchart").height(jQuery("#"+this.element_id+"-barchart").height()+50);
|
||||
bar_chart.attachEvent("onItemClick", function(id) {
|
||||
self.open_list_view(bar_chart.get(id));
|
||||
});
|
||||
@@ -394,14 +304,14 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
var chart = new dhtmlXChart({
|
||||
view:"pie3D",
|
||||
container:self.element_id+"-piechart",
|
||||
value:"#"+self.operator_field+"#",
|
||||
value:"#"+self.ordinate+"#",
|
||||
pieInnerText:function(obj) {
|
||||
var sum = chart.sum("#"+self.operator_field+"#");
|
||||
var val = obj[self.operator_field] / sum * 100 ;
|
||||
var sum = chart.sum("#"+self.ordinate+"#");
|
||||
var val = obj[self.ordinate] / sum * 100 ;
|
||||
return val.toFixed(1) + "%";
|
||||
},
|
||||
tooltip:{
|
||||
template:"#"+self.chart_info_fields+"#"+"="+"#"+self.operator_field+"#"
|
||||
template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
|
||||
},
|
||||
gradient:"3d",
|
||||
height: 20,
|
||||
@@ -416,9 +326,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
width:12
|
||||
},
|
||||
template:function(obj){
|
||||
var val = obj[self.chart_info_fields];
|
||||
val = (typeof val == 'object')?val[1]:val;
|
||||
return val;
|
||||
return obj[self.abscissa] || 'Undefined';
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -432,20 +340,13 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
if($(".dhx_tooltip").is(":visible")) {
|
||||
$(".dhx_tooltip").remove('div');
|
||||
}
|
||||
id = id[this.chart_info_fields];
|
||||
id = id[this.abscissa];
|
||||
if (typeof id == 'object'){
|
||||
id = id[0];
|
||||
}
|
||||
|
||||
var record_id = "";
|
||||
this.dataset.model = this.model;
|
||||
if (typeof this.chart_info_fields == 'object'){
|
||||
record_id = this.chart_info_fields[0];
|
||||
}else{
|
||||
record_id = this.chart_info_fields;
|
||||
}
|
||||
this.dataset.domain = [[record_id, '=', id],['id','in',this.dataset.ids]];
|
||||
var modes = !!modes ? modes.split(",") : ["list", "form", "graph"];
|
||||
var record_id = this.abscissa;
|
||||
var modes = ["list", "form", "graph"];
|
||||
var views = [];
|
||||
_.each(modes, function(mode) {
|
||||
var view = [false, mode];
|
||||
@@ -456,7 +357,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
});
|
||||
this.do_action({
|
||||
"res_model" : this.dataset.model,
|
||||
"domain" : this.dataset.domain,
|
||||
"domain" : [[record_id, '=', id], ['id','in',this.dataset.ids]],
|
||||
"views" : views,
|
||||
"type" : "ir.actions.act_window",
|
||||
"auto_search" : true,
|
||||
@@ -472,18 +373,18 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
contexts: contexts,
|
||||
group_by_seq: groupbys
|
||||
}, function (results) {
|
||||
// TODO: handle non-empty results.group_by with read_group
|
||||
if(results.group_by && results.group_by != ''){
|
||||
self.chart_info_fields = results.group_by[0];
|
||||
}else{
|
||||
self.chart_info_fields = self.chart_info;
|
||||
// TODO: handle non-empty results.group_by with read_group?
|
||||
if(!_(results.group_by).isEmpty()){
|
||||
self.abscissa = results.group_by[0];
|
||||
} else {
|
||||
self.abscissa = self.first_field;
|
||||
}
|
||||
self.dataset.context = results.context;
|
||||
self.dataset.domain = results.domain;
|
||||
self.dataset.read_slice([],{}, $.proxy(self, 'load_chart'));
|
||||
self.dataset.read_slice(self.list_fields(), {
|
||||
context: results.context,
|
||||
domain: results.domain
|
||||
}, $.proxy(self, 'schedule_chart'));
|
||||
});
|
||||
}
|
||||
});
|
||||
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
|
||||
};
|
||||
// vim:et fdc=0 fdl=0:
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"js": ["static/*/*.js", "static/*/js/*.js"],
|
||||
"css": [],
|
||||
'active': False,
|
||||
'web_preload': True,
|
||||
'web_preload': False,
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Tests",
|
||||
"version": "2.0",
|
||||
"depends": [],
|
||||
"js": ["static/src/js/*.js"],
|
||||
"css": ['static/src/css/*.css'],
|
||||
'active': True,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.oe-bunchaforms > div {
|
||||
float: left;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
openerp.web_tests = function (db) {
|
||||
db.web.client_actions.add(
|
||||
'buncha-forms', 'instance.web_tests.BunchaForms');
|
||||
db.web_tests = {};
|
||||
db.web_tests.BunchaForms = db.web.Widget.extend({
|
||||
init: function (parent) {
|
||||
this._super(parent);
|
||||
this.dataset = new db.web.DataSetSearch(this, 'test.listview.relations');
|
||||
this.form = new db.web.FormView(this, this.dataset, false, {
|
||||
action_buttons: false,
|
||||
pager: false
|
||||
});
|
||||
this.form.registry = db.web.form.readonly;
|
||||
},
|
||||
render: function () {
|
||||
return '<div class="oe-bunchaforms"></div>';
|
||||
},
|
||||
start: function () {
|
||||
$.when(
|
||||
this.dataset.read_slice(),
|
||||
this.form.appendTo(this.$element)).then(this.on_everything_loaded);
|
||||
},
|
||||
on_everything_loaded: function (slice) {
|
||||
var records = slice[0].records;
|
||||
if (!records.length) {
|
||||
this.form.on_record_loaded({});
|
||||
return;
|
||||
}
|
||||
this.form.on_record_loaded(records[0]);
|
||||
_(records.slice(1)).each(function (record, index) {
|
||||
this.dataset.index = index+1;
|
||||
this.form.reposition($('<div>').appendTo(this.$element));
|
||||
this.form.on_record_loaded(record);
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"simple": {
|
||||
"format": "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": "DEBUG",
|
||||
"formatter": "simple",
|
||||
"stream": "ext://sys.stdout"
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"web": {
|
||||
},
|
||||
"web.common.openerplib": {
|
||||
"level": "INFO"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console"]
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -2,6 +2,7 @@
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import tempfile
|
||||
import logging
|
||||
import logging.config
|
||||
@@ -26,6 +27,8 @@ optparser.add_option("--db-filter", dest="dbfilter", default='.*',
|
||||
help="Filter listed database", metavar="REGEXP")
|
||||
optparser.add_option('--addons-path', dest='addons_path', default=[path_addons], action='append',
|
||||
help="Path do addons directory", metavar="PATH")
|
||||
optparser.add_option('--load', dest='server_wide_modules', default=['web'], action='append',
|
||||
help="Load a additional module before login (by default only 'web' is loaded)", metavar="MODULE")
|
||||
|
||||
server_options = optparse.OptionGroup(optparser, "Server configuration")
|
||||
server_options.add_option("-p", "--port", dest="socket_port", default=8002,
|
||||
@@ -37,7 +40,7 @@ server_options.add_option('--no-serve-static', dest='serve_static',
|
||||
default=True, action='store_false',
|
||||
help="Do not serve static files via this server")
|
||||
server_options.add_option('--multi-threaded', dest='threaded',
|
||||
default=False, action='store_true',
|
||||
default=True, action='store_true',
|
||||
help="Spawn one thread per HTTP request")
|
||||
server_options.add_option('--proxy-mode', dest='proxy_mode',
|
||||
default=False, action='store_true',
|
||||
@@ -48,7 +51,7 @@ logging_opts = optparse.OptionGroup(optparser, "Logging")
|
||||
logging_opts.add_option("--log-level", dest="log_level", type="choice",
|
||||
default='debug', help="Global logging level", metavar="LOG_LEVEL",
|
||||
choices=['debug', 'info', 'warning', 'error', 'critical'])
|
||||
logging_opts.add_option("--log-config", dest="log_config",
|
||||
logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(os.path.dirname(__file__), "logging.json"),
|
||||
help="Logging configuration file", metavar="FILE")
|
||||
optparser.add_option_group(logging_opts)
|
||||
|
||||
@@ -60,10 +63,13 @@ if __name__ == "__main__":
|
||||
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
if not options.log_config:
|
||||
logging.basicConfig(level=getattr(logging, options.log_level.upper()))
|
||||
if sys.version_info >= (2, 7):
|
||||
with open(options.log_config) as file:
|
||||
dct = json.load(file)
|
||||
logging.config.dictConfig(dct)
|
||||
logging.getLogger("").setLevel(getattr(logging, options.log_level.upper()))
|
||||
else:
|
||||
logging.config.fileConfig(options.log_config)
|
||||
logging.basicConfig(level=getattr(logging, options.log_level.upper()))
|
||||
|
||||
app = web.common.dispatch.Root(options)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user