[MERGE] from trunk
bzr revid: xmo@openerp.com-20120322143331-iajlifomw1hohbht
33
.bzrignore
|
@ -1,23 +1,12 @@
|
|||
*.pyc
|
||||
.*.swp
|
||||
.bzrignore
|
||||
openerp/addons/*
|
||||
openerp/filestore*
|
||||
.Python
|
||||
include
|
||||
lib
|
||||
bin/activate
|
||||
bin/activate_this.py
|
||||
bin/easy_install
|
||||
bin/easy_install-2.6
|
||||
bin/pip
|
||||
bin/python
|
||||
bin/python2.6
|
||||
*.pyc
|
||||
*.pyo
|
||||
.*
|
||||
*.egg-info
|
||||
*.orig
|
||||
*.vim
|
||||
build/
|
||||
bin/yolk
|
||||
bin/pil*.py
|
||||
.project
|
||||
.pydevproject
|
||||
.settings
|
||||
RE:^bin/
|
||||
RE:^dist/
|
||||
RE:^include/
|
||||
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
This module provides the core of the OpenERP web client.
|
||||
""",
|
||||
"depends" : [],
|
||||
'active': True,
|
||||
'auto_install': True,
|
||||
'post_load' : 'wsgi_postload',
|
||||
'js' : [
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
|
@ -15,7 +15,8 @@
|
|||
"static/lib/datejs/parser.js",
|
||||
"static/lib/datejs/sugarpak.js",
|
||||
"static/lib/datejs/extras.js",
|
||||
"static/lib/jquery/jquery-1.6.4.js",
|
||||
#"static/lib/jquery/jquery-1.6.4.js",
|
||||
"static/lib/jquery/jquery-1.7.2b1.js",
|
||||
"static/lib/jquery.MD5/jquery.md5.js",
|
||||
"static/lib/jquery.form/jquery.form.js",
|
||||
"static/lib/jquery.validate/jquery.validate.js",
|
||||
|
@ -35,7 +36,8 @@
|
|||
"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/lib/py.js/lib/py.js",
|
||||
"static/lib/novajs/src/nova.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/core.js",
|
||||
"static/src/js/dates.js",
|
||||
|
@ -59,6 +61,7 @@
|
|||
"static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
|
||||
"static/lib/jquery.ui.notify/css/ui.notify.css",
|
||||
"static/lib/jquery.tipsy/tipsy.css",
|
||||
"static/src/css/base_old.css",
|
||||
"static/src/css/base.css",
|
||||
"static/src/css/data_export.css",
|
||||
"static/src/css/data_import.css",
|
||||
|
|
|
@ -98,19 +98,72 @@ html_template = """<!DOCTYPE html>
|
|||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP</title>
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="/web/static/src/css/full.css" />
|
||||
%(css)s
|
||||
%(js)s
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var s = new openerp.init(%(modules)s);
|
||||
%(init)s
|
||||
var wc = new s.web.WebClient();
|
||||
wc.appendTo($(document.body));
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body class="openerp" id="oe"></body>
|
||||
<body></body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
def sass2scss(src):
|
||||
# Validated by diff -u of sass2scss against:
|
||||
# sass-convert -F sass -T scss openerp.sass openerp.scss
|
||||
block = []
|
||||
sass = ('', block)
|
||||
reComment = re.compile(r'//.*$')
|
||||
reIndent = re.compile(r'^\s+')
|
||||
reIgnore = re.compile(r'^\s*(//.*)?$')
|
||||
reFixes = { re.compile(r'\(\((.*)\)\)') : r'(\1)', }
|
||||
lastLevel = 0
|
||||
prevBlocks = {}
|
||||
for l in src.split('\n'):
|
||||
l = l.rstrip()
|
||||
if reIgnore.search(l): continue
|
||||
l = reComment.sub('', l)
|
||||
l = l.rstrip()
|
||||
indent = reIndent.match(l)
|
||||
level = indent.end() if indent else 0
|
||||
l = l[level:]
|
||||
if level>lastLevel:
|
||||
prevBlocks[lastLevel] = block
|
||||
newBlock = []
|
||||
block[-1] = (block[-1], newBlock)
|
||||
block = newBlock
|
||||
elif level<lastLevel:
|
||||
block = prevBlocks[level]
|
||||
lastLevel = level
|
||||
if not l: continue
|
||||
# Fixes
|
||||
for ereg, repl in reFixes.items():
|
||||
l = ereg.sub(repl if type(repl)==str else repl(), l)
|
||||
block.append(l)
|
||||
|
||||
def write(sass, level=-1):
|
||||
out = ""
|
||||
indent = ' '*level
|
||||
if type(sass)==tuple:
|
||||
if level>=0:
|
||||
out += indent+sass[0]+" {\n"
|
||||
for e in sass[1]:
|
||||
out += write(e, level+1)
|
||||
if level>=0:
|
||||
out = out.rstrip(" \n")
|
||||
out += ' }\n'
|
||||
if level==0:
|
||||
out += "\n"
|
||||
else:
|
||||
out += indent+sass+";\n"
|
||||
return out
|
||||
return write(sass)
|
||||
|
||||
class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/web/webclient"
|
||||
|
||||
|
@ -270,7 +323,6 @@ class WebClient(openerpweb.Controller):
|
|||
'js': js,
|
||||
'css': css,
|
||||
'modules': simplejson.dumps(self.server_wide_modules(req)),
|
||||
'init': 'new s.web.WebClient().start();',
|
||||
}
|
||||
return r
|
||||
|
||||
|
@ -351,19 +403,14 @@ class Database(openerpweb.Controller):
|
|||
def get_list(self, req):
|
||||
proxy = req.session.proxy("db")
|
||||
dbs = proxy.list()
|
||||
h = req.httprequest.headers['Host'].split(':')[0]
|
||||
h = req.httprequest.environ['HTTP_HOST'].split(':')[0]
|
||||
d = h.split('.')[0]
|
||||
r = req.config.dbfilter.replace('%h', h).replace('%d', d)
|
||||
dbs = [i for i in dbs if re.match(r, i)]
|
||||
return {"db_list": dbs}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def progress(self, req, password, id):
|
||||
return req.session.proxy('db').get_progress(password, id)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def create(self, req, fields):
|
||||
|
||||
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
||||
create_attrs = (
|
||||
params['super_admin_pwd'],
|
||||
|
@ -373,17 +420,7 @@ class Database(openerpweb.Controller):
|
|||
params['create_admin_pwd']
|
||||
)
|
||||
|
||||
try:
|
||||
return req.session.proxy("db").create(*create_attrs)
|
||||
except xmlrpclib.Fault, e:
|
||||
if e.faultCode and isinstance(e.faultCode, str)\
|
||||
and e.faultCode.split(':')[0] == 'AccessDenied':
|
||||
return {'error': e.faultCode, 'title': 'Database creation error'}
|
||||
return {
|
||||
'error': "Could not create database '%s': %s" % (
|
||||
params['db_name'], e.faultString),
|
||||
'title': 'Database creation error'
|
||||
}
|
||||
return req.session.proxy("db").create_database(*create_attrs)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def drop(self, req, fields):
|
||||
|
@ -435,6 +472,50 @@ class Database(openerpweb.Controller):
|
|||
return {'error': e.faultCode, 'title': 'Change Password'}
|
||||
return {'error': 'Error, password not changed !', 'title': 'Change Password'}
|
||||
|
||||
def topological_sort(modules):
|
||||
""" Return a list of module names sorted so that their dependencies of the
|
||||
modules are listed before the module itself
|
||||
|
||||
modules is a dict of {module_name: dependencies}
|
||||
|
||||
:param modules: modules to sort
|
||||
:type modules: dict
|
||||
:returns: list(str)
|
||||
"""
|
||||
|
||||
dependencies = set(itertools.chain.from_iterable(modules.itervalues()))
|
||||
# incoming edge: dependency on other module (if a depends on b, a has an
|
||||
# incoming edge from b, aka there's an edge from b to a)
|
||||
# outgoing edge: other module depending on this one
|
||||
|
||||
# [Tarjan 1976], http://en.wikipedia.org/wiki/Topological_sorting#Algorithms
|
||||
#L ← Empty list that will contain the sorted nodes
|
||||
L = []
|
||||
#S ← Set of all nodes with no outgoing edges (modules on which no other
|
||||
# module depends)
|
||||
S = set(module for module in modules if module not in dependencies)
|
||||
|
||||
visited = set()
|
||||
#function visit(node n)
|
||||
def visit(n):
|
||||
#if n has not been visited yet then
|
||||
if n not in visited:
|
||||
#mark n as visited
|
||||
visited.add(n)
|
||||
#change: n not web module, can not be resolved, ignore
|
||||
if n not in modules: return
|
||||
#for each node m with an edge from m to n do (dependencies of n)
|
||||
for m in modules[n]:
|
||||
#visit(m)
|
||||
visit(m)
|
||||
#add n to L
|
||||
L.append(n)
|
||||
#for each node n in S do
|
||||
for n in S:
|
||||
#visit(n)
|
||||
visit(n)
|
||||
return L
|
||||
|
||||
class Session(openerpweb.Controller):
|
||||
_cp_path = "/web/session"
|
||||
|
||||
|
@ -502,20 +583,32 @@ class Session(openerpweb.Controller):
|
|||
def modules(self, req):
|
||||
# Compute available candidates module
|
||||
loadable = openerpweb.addons_manifest
|
||||
loaded = req.config.server_wide_modules
|
||||
loaded = set(req.config.server_wide_modules)
|
||||
candidates = [mod for mod in loadable if mod not in loaded]
|
||||
|
||||
# Compute active true modules that might be on the web side only
|
||||
active = set(name for name in candidates
|
||||
if openerpweb.addons_manifest[name].get('active'))
|
||||
# already installed modules have no dependencies
|
||||
modules = dict.fromkeys(loaded, [])
|
||||
|
||||
# Compute auto_install modules that might be on the web side only
|
||||
modules.update((name, openerpweb.addons_manifest[name].get('depends', []))
|
||||
for name in candidates
|
||||
if openerpweb.addons_manifest[name].get('auto_install'))
|
||||
|
||||
# Retrieve database installed modules
|
||||
Modules = req.session.model('ir.module.module')
|
||||
installed = set(module['name'] for module in Modules.search_read(
|
||||
[('state','=','installed'), ('name','in', candidates)], ['name']))
|
||||
for module in Modules.search_read(
|
||||
[('state','=','installed'), ('name','in', candidates)],
|
||||
['name', 'dependencies_id']):
|
||||
deps = module.get('dependencies_id')
|
||||
if deps:
|
||||
dependencies = map(
|
||||
operator.itemgetter('name'),
|
||||
req.session.model('ir.module.module.dependency').read(deps, ['name']))
|
||||
modules[module['name']] = list(
|
||||
set(modules.get(module['name'], []) + dependencies))
|
||||
|
||||
# Merge both
|
||||
return list(active | installed)
|
||||
sorted_modules = topological_sort(modules)
|
||||
return [module for module in sorted_modules if module not in loaded]
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def eval_domain_and_context(self, req, contexts, domains,
|
||||
|
@ -762,11 +855,13 @@ class Menu(openerpweb.Controller):
|
|||
Menus = s.model('ir.ui.menu')
|
||||
# If a menu action is defined use its domain to get the root menu items
|
||||
user_menu_id = s.model('res.users').read([s._uid], ['menu_id'], context)[0]['menu_id']
|
||||
|
||||
menu_domain = [('parent_id', '=', False)]
|
||||
if user_menu_id:
|
||||
menu_domain = s.model('ir.actions.act_window').read([user_menu_id[0]], ['domain'], context)[0]['domain']
|
||||
menu_domain = ast.literal_eval(menu_domain)
|
||||
else:
|
||||
menu_domain = [('parent_id', '=', False)]
|
||||
domain_string = s.model('ir.actions.act_window').read([user_menu_id[0]], ['domain'], context)[0]['domain']
|
||||
if domain_string:
|
||||
menu_domain = ast.literal_eval(domain_string)
|
||||
|
||||
return Menus.search(menu_domain, 0, False, False, context)
|
||||
|
||||
def do_load(self, req):
|
||||
|
@ -780,13 +875,13 @@ class Menu(openerpweb.Controller):
|
|||
context = req.session.eval_context(req.context)
|
||||
Menus = req.session.model('ir.ui.menu')
|
||||
|
||||
menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id'], context)
|
||||
menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action'], context)
|
||||
menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, ''], 'children' : menu_roots}
|
||||
|
||||
# menus are loaded fully unlike a regular tree view, cause there are a
|
||||
# limited number of items (752 when all 6.1 addons are installed)
|
||||
menu_ids = Menus.search([], 0, False, False, context)
|
||||
menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context)
|
||||
menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action'], context)
|
||||
# adds roots at the end of the sequence, so that they will overwrite
|
||||
# equivalent menu items from full menu read when put into id:item
|
||||
# mapping, resulting in children being correctly set on the roots.
|
||||
|
@ -1179,10 +1274,15 @@ class SearchView(View):
|
|||
filters = Model.get_filters(model)
|
||||
for filter in filters:
|
||||
try:
|
||||
filter["context"] = req.session.eval_context(
|
||||
parse_context(filter["context"], req.session))
|
||||
filter["domain"] = req.session.eval_domain(
|
||||
parse_domain(filter["domain"], req.session))
|
||||
parsed_context = parse_context(filter["context"], req.session)
|
||||
filter["context"] = (parsed_context
|
||||
if not isinstance(parsed_context, common.nonliterals.BaseContext)
|
||||
else req.session.eval_context(parsed_context))
|
||||
|
||||
parsed_domain = parse_domain(filter["domain"], req.session)
|
||||
filter["domain"] = (parsed_domain
|
||||
if not isinstance(parsed_domain, common.nonliterals.BaseDomain)
|
||||
else req.session.eval_domain(parsed_domain))
|
||||
except Exception:
|
||||
logger.exception("Failed to parse custom filter %s in %s",
|
||||
filter['name'], model)
|
||||
|
@ -1215,6 +1315,7 @@ class SearchView(View):
|
|||
ctx = common.nonliterals.CompoundContext(context_to_save)
|
||||
ctx.session = req.session
|
||||
ctx = ctx.evaluate()
|
||||
ctx['dashboard_merge_domains_contexts'] = False # TODO: replace this 6.1 workaround by attribute on <action/>
|
||||
domain = common.nonliterals.CompoundDomain(domain)
|
||||
domain.session = req.session
|
||||
domain = domain.evaluate()
|
||||
|
@ -1230,7 +1331,7 @@ class SearchView(View):
|
|||
if board and 'arch' in board:
|
||||
xml = ElementTree.fromstring(board['arch'])
|
||||
column = xml.find('./board/column')
|
||||
if column:
|
||||
if column is not None:
|
||||
new_action = ElementTree.Element('action', {
|
||||
'name' : str(action_id),
|
||||
'string' : name,
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-15 07:48+0000\n"
|
||||
"Last-Translator: Jorge L Tupac-Yupanqui <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-16 11:00+0000\n"
|
||||
"Last-Translator: Vicente <jviares@gmail.com>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -657,7 +657,7 @@ msgstr "Traducciones"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
msgid "Powered by"
|
||||
msgstr "Desarrollado por"
|
||||
msgstr "Con tecnoloxía de"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
|
@ -1118,7 +1118,7 @@ msgstr "Limpiar"
|
|||
#: addons/web/static/src/xml/base.xml:1172
|
||||
#: addons/web/static/src/xml/base.xml:1223
|
||||
msgid "Uploading ..."
|
||||
msgstr "Subiendo ..."
|
||||
msgstr "Cargando..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1200
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-10 20:39+0000\n"
|
||||
"PO-Revision-Date: 2012-02-16 19:06+0000\n"
|
||||
"Last-Translator: Freddy Gonzalez <freddy.gonzalez@clearcorp.co.cr>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#. openerp-web
|
||||
|
@ -194,7 +194,7 @@ msgstr "Descargar \"%s\""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:191
|
||||
msgid "Filter disabled due to invalid syntax"
|
||||
msgstr ""
|
||||
msgstr "Filtro desactivado debido a sintaxis inválida"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:237
|
||||
|
@ -384,12 +384,12 @@ msgstr "Ver Editor de%d -%s"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:367
|
||||
msgid "Inherited View"
|
||||
msgstr ""
|
||||
msgstr "Vista heredada"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:371
|
||||
msgid "Do you really wants to create an inherited view here?"
|
||||
msgstr ""
|
||||
msgstr "¿Realmente desea crear una vista heredada aquí?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:381
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-13 23:41+0000\n"
|
||||
"Last-Translator: Amós Oviedo <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-16 10:02+0000\n"
|
||||
"Last-Translator: Vicente <jviares@gmail.com>\n"
|
||||
"Language-Team: Galician <gl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -193,7 +193,7 @@ msgstr "Descargar \"%s\""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:191
|
||||
msgid "Filter disabled due to invalid syntax"
|
||||
msgstr ""
|
||||
msgstr "Filtro desactivado debido a sintaxis inválida"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:237
|
||||
|
@ -383,12 +383,12 @@ msgstr "Ver Editor %d - %s"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:367
|
||||
msgid "Inherited View"
|
||||
msgstr ""
|
||||
msgstr "Vista Herdada"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:371
|
||||
msgid "Do you really wants to create an inherited view here?"
|
||||
msgstr ""
|
||||
msgstr "¿Realmente quere crear unha vista herdada aquí?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:381
|
||||
|
@ -427,7 +427,7 @@ msgstr "Persoalizar"
|
|||
#: addons/web/static/src/js/view_form.js:123
|
||||
#: addons/web/static/src/js/view_form.js:686
|
||||
msgid "Set Default"
|
||||
msgstr ""
|
||||
msgstr "Estabelecer como predeterminado"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:469
|
||||
|
@ -438,7 +438,7 @@ msgstr "Advertencia, o rexistro modificouse, os cambios serán descartados."
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:693
|
||||
msgid "Save default"
|
||||
msgstr ""
|
||||
msgstr "Gardar como predeterminado"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:754
|
||||
|
@ -904,47 +904,47 @@ msgstr "Vista"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:484
|
||||
msgid "Edit SearchView"
|
||||
msgstr ""
|
||||
msgstr "Editar SearchView"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:485
|
||||
msgid "Edit Action"
|
||||
msgstr ""
|
||||
msgstr "Editar a acción"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:486
|
||||
msgid "Edit Workflow"
|
||||
msgstr ""
|
||||
msgstr "Editar Fluxo de Traballo"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:491
|
||||
msgid "ID:"
|
||||
msgstr ""
|
||||
msgstr "ID:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:494
|
||||
msgid "XML ID:"
|
||||
msgstr ""
|
||||
msgstr "XML ID:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:497
|
||||
msgid "Creation User:"
|
||||
msgstr ""
|
||||
msgstr "Usuario de Creación:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:500
|
||||
msgid "Creation Date:"
|
||||
msgstr ""
|
||||
msgstr "Data de Creación:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:503
|
||||
msgid "Latest Modification by:"
|
||||
msgstr ""
|
||||
msgstr "Última Modificación por:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:506
|
||||
msgid "Latest Modification Date:"
|
||||
msgstr ""
|
||||
msgstr "Última data de modificación:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:542
|
||||
|
@ -966,27 +966,27 @@ msgstr "Duplicar"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:775
|
||||
msgid "Add attachment"
|
||||
msgstr ""
|
||||
msgstr "Engadir anexo"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:801
|
||||
msgid "Default:"
|
||||
msgstr ""
|
||||
msgstr "Predeterminado:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:818
|
||||
msgid "Condition:"
|
||||
msgstr ""
|
||||
msgstr "Estado:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:837
|
||||
msgid "Only you"
|
||||
msgstr ""
|
||||
msgstr "So vostede"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:844
|
||||
msgid "All users"
|
||||
msgstr ""
|
||||
msgstr "Todos os usuarios"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:851
|
||||
|
@ -996,88 +996,88 @@ msgstr "Widget non controlado"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:900
|
||||
msgid "Notebook Page \""
|
||||
msgstr ""
|
||||
msgstr "Páxina de Caderno \""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:905
|
||||
#: addons/web/static/src/xml/base.xml:964
|
||||
msgid "Modifiers:"
|
||||
msgstr ""
|
||||
msgstr "Modificadores:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:931
|
||||
msgid "(nolabel)"
|
||||
msgstr ""
|
||||
msgstr "(sin etiqueta)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:936
|
||||
msgid "Field:"
|
||||
msgstr ""
|
||||
msgstr "Campo:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:940
|
||||
msgid "Object:"
|
||||
msgstr ""
|
||||
msgstr "Obxecto:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:944
|
||||
msgid "Type:"
|
||||
msgstr ""
|
||||
msgstr "Tipo:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:948
|
||||
msgid "Widget:"
|
||||
msgstr ""
|
||||
msgstr "Widget:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:952
|
||||
msgid "Size:"
|
||||
msgstr ""
|
||||
msgstr "Tamaño:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:956
|
||||
msgid "Context:"
|
||||
msgstr ""
|
||||
msgstr "Contexto:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:960
|
||||
msgid "Domain:"
|
||||
msgstr ""
|
||||
msgstr "Dominio:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:968
|
||||
msgid "Change default:"
|
||||
msgstr ""
|
||||
msgstr "Cambiar predeterminado:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:972
|
||||
msgid "On change:"
|
||||
msgstr ""
|
||||
msgstr "Cando cambie:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:976
|
||||
msgid "Relation:"
|
||||
msgstr ""
|
||||
msgstr "Relación:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:980
|
||||
msgid "Selection:"
|
||||
msgstr ""
|
||||
msgstr "Selección:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1020
|
||||
msgid "Send an e-mail with your default e-mail client"
|
||||
msgstr ""
|
||||
msgstr "Envíe un correo electrónico co seu cliente de correo predefinido"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1034
|
||||
msgid "Open this resource"
|
||||
msgstr ""
|
||||
msgstr "Abrir este recurso"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1056
|
||||
msgid "Select date"
|
||||
msgstr ""
|
||||
msgstr "Seleccionar data"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1090
|
||||
|
@ -1103,7 +1103,7 @@ msgstr "…"
|
|||
#: addons/web/static/src/xml/base.xml:1155
|
||||
#: addons/web/static/src/xml/base.xml:1198
|
||||
msgid "Set Image"
|
||||
msgstr ""
|
||||
msgstr "Estabelecer imaxe"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1163
|
||||
|
@ -1134,57 +1134,57 @@ msgstr "Gardar como"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1238
|
||||
msgid "Button"
|
||||
msgstr ""
|
||||
msgstr "Botón"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1241
|
||||
msgid "(no string)"
|
||||
msgstr ""
|
||||
msgstr "(sin cadena)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1248
|
||||
msgid "Special:"
|
||||
msgstr ""
|
||||
msgstr "Especial:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1253
|
||||
msgid "Button Type:"
|
||||
msgstr ""
|
||||
msgstr "Tipo de Botón"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1257
|
||||
msgid "Method:"
|
||||
msgstr ""
|
||||
msgstr "Método:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1261
|
||||
msgid "Action ID:"
|
||||
msgstr ""
|
||||
msgstr "Acción ID:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1271
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
msgstr "Busca"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1279
|
||||
msgid "Filters"
|
||||
msgstr ""
|
||||
msgstr "Filtros"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1280
|
||||
msgid "-- Filters --"
|
||||
msgstr ""
|
||||
msgstr "-- Filtros --"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1289
|
||||
msgid "-- Actions --"
|
||||
msgstr ""
|
||||
msgstr "-- Accions --"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1290
|
||||
msgid "Add Advanced Filter"
|
||||
msgstr ""
|
||||
msgstr "Engadir Filtro Avanzado"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1291
|
||||
|
@ -1209,17 +1209,17 @@ msgstr "(Calquer filtro existente co mesmo nome será reemplazado)"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1305
|
||||
msgid "Select Dashboard to add this filter to:"
|
||||
msgstr ""
|
||||
msgstr "Seleccionar tableiro para engadirlle este filtro:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1309
|
||||
msgid "Title of new Dashboard item:"
|
||||
msgstr ""
|
||||
msgstr "Título de novo elemento de Tableiro:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1416
|
||||
msgid "Advanced Filters"
|
||||
msgstr ""
|
||||
msgstr "Filtros Avanzados"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1426
|
||||
|
@ -1411,6 +1411,8 @@ msgid ""
|
|||
"For use if CSV files have titles on multiple lines, skips more than a single "
|
||||
"line during import"
|
||||
msgstr ""
|
||||
"Para o seu uso se os ficheiros CSV teñen títulos en varias liñas, omitense "
|
||||
"mais de una soa liña durante a importación"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1803
|
||||
|
@ -1425,7 +1427,7 @@ msgstr "Esta é a vista previa do arquivo que non se pode importar:"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1812
|
||||
msgid "Activate the developper mode"
|
||||
msgstr ""
|
||||
msgstr "Activar modo de desenvolvedor"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1814
|
||||
|
@ -1435,7 +1437,7 @@ msgstr "Versión"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1815
|
||||
msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved."
|
||||
msgstr ""
|
||||
msgstr "Copyright © 2004-HOY OpenERP SA. Todos os dereitos reservados."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1816
|
||||
|
@ -1460,9 +1462,9 @@ msgstr "GNU Affero General Public License"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1822
|
||||
msgid "For more information visit"
|
||||
msgstr ""
|
||||
msgstr "Para mais información visite"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1823
|
||||
msgid "OpenERP.com"
|
||||
msgstr ""
|
||||
msgstr "OpenERP.com"
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-11 21:36+0000\n"
|
||||
"PO-Revision-Date: 2012-03-14 07:11+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-15 04:53+0000\n"
|
||||
"X-Generator: Launchpad (build 14933)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -24,8 +24,10 @@ msgstr ""
|
|||
#: addons/web/static/src/js/view_form.js:419
|
||||
#: addons/web/static/src/js/view_form.js:1233
|
||||
#: addons/web/static/src/xml/base.xml:1695
|
||||
#: addons/web/static/src/js/view_form.js:424
|
||||
#: addons/web/static/src/js/view_form.js:1239
|
||||
msgid "Ok"
|
||||
msgstr ""
|
||||
msgstr "正常"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:180
|
||||
|
@ -35,18 +37,18 @@ msgstr "OpenERPエンタープライズレポートを送る"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:194
|
||||
msgid "Dont send"
|
||||
msgstr ""
|
||||
msgstr "送らない"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:256
|
||||
#, python-format
|
||||
msgid "Loading (%d)"
|
||||
msgstr ""
|
||||
msgstr "ロード中 (%d)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:288
|
||||
msgid "Invalid database name"
|
||||
msgstr ""
|
||||
msgstr "無効なデータベース名"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:483
|
||||
|
@ -56,29 +58,29 @@ msgstr ""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:484
|
||||
msgid "Database backed up successfully"
|
||||
msgstr ""
|
||||
msgstr "データベースは正常にバックアップされました"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:527
|
||||
msgid "Restored"
|
||||
msgstr ""
|
||||
msgstr "リストアされました"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:527
|
||||
msgid "Database restored successfully"
|
||||
msgstr ""
|
||||
msgstr "データベースは正常にリストアされました"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:708
|
||||
#: addons/web/static/src/xml/base.xml:359
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
msgstr "について"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:787
|
||||
#: addons/web/static/src/xml/base.xml:356
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
msgstr "個人設定"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:790
|
||||
|
@ -92,44 +94,49 @@ msgstr ""
|
|||
#: addons/web/static/src/xml/base.xml:1496
|
||||
#: addons/web/static/src/xml/base.xml:1506
|
||||
#: addons/web/static/src/xml/base.xml:1515
|
||||
#: addons/web/static/src/js/search.js:293
|
||||
#: addons/web/static/src/js/view_form.js:1234
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgstr "キャンセル"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:791
|
||||
msgid "Change password"
|
||||
msgstr ""
|
||||
msgstr "パスワードの変更"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:792
|
||||
#: addons/web/static/src/js/view_editor.js:73
|
||||
#: addons/web/static/src/js/views.js:962 addons/web/static/src/xml/base.xml:737
|
||||
#: addons/web/static/src/js/views.js:962
|
||||
#: addons/web/static/src/xml/base.xml:737
|
||||
#: addons/web/static/src/xml/base.xml:1500
|
||||
#: addons/web/static/src/xml/base.xml:1514
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "保存"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:811
|
||||
#: addons/web/static/src/xml/base.xml:226
|
||||
#: addons/web/static/src/xml/base.xml:1729
|
||||
msgid "Change Password"
|
||||
msgstr ""
|
||||
msgstr "パスワードの変更"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:1096
|
||||
#: addons/web/static/src/js/chrome.js:1100
|
||||
msgid "OpenERP - Unsupported/Community Version"
|
||||
msgstr ""
|
||||
msgstr "OpenERP - サポート無し/コミュニティバージョン"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:1131
|
||||
#: addons/web/static/src/js/chrome.js:1135
|
||||
msgid "Client Error"
|
||||
msgstr ""
|
||||
msgstr "クライアントのエラー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:6
|
||||
msgid "Export Data"
|
||||
msgstr ""
|
||||
msgstr "データのエクスポート"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:19
|
||||
|
@ -139,13 +146,15 @@ msgstr ""
|
|||
#: addons/web/static/src/js/view_form.js:692
|
||||
#: addons/web/static/src/js/view_form.js:3044
|
||||
#: addons/web/static/src/js/views.js:963
|
||||
#: addons/web/static/src/js/view_form.js:698
|
||||
#: addons/web/static/src/js/view_form.js:3067
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
msgstr "閉じる"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:20
|
||||
msgid "Export To File"
|
||||
msgstr ""
|
||||
msgstr "ファイルにエクスポート"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:125
|
||||
|
@ -165,29 +174,32 @@ msgstr ""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_import.js:34
|
||||
msgid "Import Data"
|
||||
msgstr ""
|
||||
msgstr "データをインポート"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_import.js:70
|
||||
msgid "Import File"
|
||||
msgstr ""
|
||||
msgstr "ファイルをインポート"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_import.js:105
|
||||
msgid "External ID"
|
||||
msgstr ""
|
||||
msgstr "外部ID"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/formats.js:300
|
||||
#: addons/web/static/src/js/view_page.js:245
|
||||
#: addons/web/static/src/js/formats.js:322
|
||||
#: addons/web/static/src/js/view_page.js:251
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
msgstr "ダウンロード"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/formats.js:305
|
||||
#: addons/web/static/src/js/formats.js:327
|
||||
#, python-format
|
||||
msgid "Download \"%s\""
|
||||
msgstr ""
|
||||
msgstr "ダウンロード \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:191
|
||||
|
@ -197,66 +209,77 @@ msgstr ""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:237
|
||||
msgid "Filter Entry"
|
||||
msgstr ""
|
||||
msgstr "フィルター項目"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:242
|
||||
#: addons/web/static/src/js/search.js:291
|
||||
#: addons/web/static/src/js/search.js:296
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
msgstr "はい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:286
|
||||
#: addons/web/static/src/xml/base.xml:1292
|
||||
#: addons/web/static/src/js/search.js:291
|
||||
msgid "Add to Dashboard"
|
||||
msgstr ""
|
||||
msgstr "ダッシュボードに追加"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:415
|
||||
#: addons/web/static/src/js/search.js:420
|
||||
msgid "Invalid Search"
|
||||
msgstr ""
|
||||
msgstr "無効な検索"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:415
|
||||
#: addons/web/static/src/js/search.js:420
|
||||
msgid "triggered from search view"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:503
|
||||
#: addons/web/static/src/js/search.js:508
|
||||
#, python-format
|
||||
msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:839
|
||||
#: addons/web/static/src/js/search.js:844
|
||||
msgid "not a valid integer"
|
||||
msgstr ""
|
||||
msgstr "無効な整数"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:853
|
||||
#: addons/web/static/src/js/search.js:858
|
||||
msgid "not a valid number"
|
||||
msgstr ""
|
||||
msgstr "無効な数値"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:931
|
||||
#: addons/web/static/src/xml/base.xml:968
|
||||
#: addons/web/static/src/js/search.js:936
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
msgstr "はい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:932
|
||||
#: addons/web/static/src/js/search.js:937
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
msgstr "いいえ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1290
|
||||
#: addons/web/static/src/js/search.js:1295
|
||||
msgid "contains"
|
||||
msgstr ""
|
||||
msgstr "次を含む"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1291
|
||||
#: addons/web/static/src/js/search.js:1296
|
||||
msgid "doesn't contain"
|
||||
msgstr ""
|
||||
msgstr "含まない"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1292
|
||||
|
@ -264,8 +287,13 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1325
|
||||
#: addons/web/static/src/js/search.js:1344
|
||||
#: addons/web/static/src/js/search.js:1365
|
||||
#: addons/web/static/src/js/search.js:1297
|
||||
#: addons/web/static/src/js/search.js:1311
|
||||
#: addons/web/static/src/js/search.js:1330
|
||||
#: addons/web/static/src/js/search.js:1349
|
||||
#: addons/web/static/src/js/search.js:1370
|
||||
msgid "is equal to"
|
||||
msgstr ""
|
||||
msgstr "は次と一致する"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1293
|
||||
|
@ -273,8 +301,13 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1326
|
||||
#: addons/web/static/src/js/search.js:1345
|
||||
#: addons/web/static/src/js/search.js:1366
|
||||
#: addons/web/static/src/js/search.js:1298
|
||||
#: addons/web/static/src/js/search.js:1312
|
||||
#: addons/web/static/src/js/search.js:1331
|
||||
#: addons/web/static/src/js/search.js:1350
|
||||
#: addons/web/static/src/js/search.js:1371
|
||||
msgid "is not equal to"
|
||||
msgstr ""
|
||||
msgstr "は次と一致しない"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1294
|
||||
|
@ -282,8 +315,13 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1327
|
||||
#: addons/web/static/src/js/search.js:1346
|
||||
#: addons/web/static/src/js/search.js:1367
|
||||
#: addons/web/static/src/js/search.js:1299
|
||||
#: addons/web/static/src/js/search.js:1313
|
||||
#: addons/web/static/src/js/search.js:1332
|
||||
#: addons/web/static/src/js/search.js:1351
|
||||
#: addons/web/static/src/js/search.js:1372
|
||||
msgid "greater than"
|
||||
msgstr ""
|
||||
msgstr "次より大きい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1295
|
||||
|
@ -291,8 +329,13 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1328
|
||||
#: addons/web/static/src/js/search.js:1347
|
||||
#: addons/web/static/src/js/search.js:1368
|
||||
#: addons/web/static/src/js/search.js:1300
|
||||
#: addons/web/static/src/js/search.js:1314
|
||||
#: addons/web/static/src/js/search.js:1333
|
||||
#: addons/web/static/src/js/search.js:1352
|
||||
#: addons/web/static/src/js/search.js:1373
|
||||
msgid "less than"
|
||||
msgstr ""
|
||||
msgstr "次より小さい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1296
|
||||
|
@ -300,8 +343,13 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1329
|
||||
#: addons/web/static/src/js/search.js:1348
|
||||
#: addons/web/static/src/js/search.js:1369
|
||||
#: addons/web/static/src/js/search.js:1301
|
||||
#: addons/web/static/src/js/search.js:1315
|
||||
#: addons/web/static/src/js/search.js:1334
|
||||
#: addons/web/static/src/js/search.js:1353
|
||||
#: addons/web/static/src/js/search.js:1374
|
||||
msgid "greater or equal than"
|
||||
msgstr ""
|
||||
msgstr "次より大きいか等しい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1297
|
||||
|
@ -309,27 +357,37 @@ msgstr ""
|
|||
#: addons/web/static/src/js/search.js:1330
|
||||
#: addons/web/static/src/js/search.js:1349
|
||||
#: addons/web/static/src/js/search.js:1370
|
||||
#: addons/web/static/src/js/search.js:1302
|
||||
#: addons/web/static/src/js/search.js:1316
|
||||
#: addons/web/static/src/js/search.js:1335
|
||||
#: addons/web/static/src/js/search.js:1354
|
||||
#: addons/web/static/src/js/search.js:1375
|
||||
msgid "less or equal than"
|
||||
msgstr ""
|
||||
msgstr "次より小さいか等しい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1360
|
||||
#: addons/web/static/src/js/search.js:1383
|
||||
#: addons/web/static/src/js/search.js:1365
|
||||
#: addons/web/static/src/js/search.js:1388
|
||||
msgid "is"
|
||||
msgstr ""
|
||||
msgstr "が次である"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1384
|
||||
#: addons/web/static/src/js/search.js:1389
|
||||
msgid "is not"
|
||||
msgstr ""
|
||||
msgstr "は次ではない"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1396
|
||||
#: addons/web/static/src/js/search.js:1401
|
||||
msgid "is true"
|
||||
msgstr ""
|
||||
msgstr "は正しい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:1397
|
||||
#: addons/web/static/src/js/search.js:1402
|
||||
msgid "is false"
|
||||
msgstr ""
|
||||
|
||||
|
@ -424,51 +482,60 @@ msgstr ""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:123
|
||||
#: addons/web/static/src/js/view_form.js:686
|
||||
#: addons/web/static/src/js/view_form.js:692
|
||||
msgid "Set Default"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:469
|
||||
#: addons/web/static/src/js/view_form.js:475
|
||||
msgid ""
|
||||
"Warning, the record has been modified, your changes will be discarded."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:693
|
||||
#: addons/web/static/src/js/view_form.js:699
|
||||
msgid "Save default"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:754
|
||||
#: addons/web/static/src/js/view_form.js:760
|
||||
msgid "Attachments"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:792
|
||||
#: addons/web/static/src/js/view_form.js:798
|
||||
#, python-format
|
||||
msgid "Do you really want to delete the attachment %s?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:822
|
||||
#: addons/web/static/src/js/view_form.js:828
|
||||
#, python-format
|
||||
msgid "Unknown operator %s in domain %s"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:830
|
||||
#: addons/web/static/src/js/view_form.js:836
|
||||
#, python-format
|
||||
msgid "Unknown field %s in domain %s"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:868
|
||||
#: addons/web/static/src/js/view_form.js:874
|
||||
#, python-format
|
||||
msgid "Unsupported operator %s in domain %s"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:1225
|
||||
#: addons/web/static/src/js/view_form.js:1231
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
|
@ -476,34 +543,43 @@ msgstr ""
|
|||
#: addons/web/static/src/js/view_form.js:1921
|
||||
#: addons/web/static/src/js/view_form.js:2578
|
||||
#: addons/web/static/src/js/view_form.js:2741
|
||||
#: addons/web/static/src/js/view_form.js:1933
|
||||
#: addons/web/static/src/js/view_form.js:2590
|
||||
#: addons/web/static/src/js/view_form.js:2760
|
||||
msgid "Open: "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2049
|
||||
#: addons/web/static/src/js/view_form.js:2061
|
||||
msgid "<em> Search More...</em>"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2062
|
||||
#: addons/web/static/src/js/view_form.js:2074
|
||||
#, python-format
|
||||
msgid "<em> Create \"<strong>%s</strong>\"</em>"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2068
|
||||
#: addons/web/static/src/js/view_form.js:2080
|
||||
msgid "<em> Create and Edit...</em>"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2101
|
||||
#: addons/web/static/src/js/views.js:675
|
||||
#: addons/web/static/src/js/view_form.js:2113
|
||||
msgid "Search: "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2101
|
||||
#: addons/web/static/src/js/view_form.js:2550
|
||||
#: addons/web/static/src/js/view_form.js:2113
|
||||
#: addons/web/static/src/js/view_form.js:2562
|
||||
msgid "Create: "
|
||||
msgstr ""
|
||||
|
||||
|
@ -512,11 +588,13 @@ msgstr ""
|
|||
#: addons/web/static/src/xml/base.xml:750
|
||||
#: addons/web/static/src/xml/base.xml:772
|
||||
#: addons/web/static/src/xml/base.xml:1646
|
||||
#: addons/web/static/src/js/view_form.js:2680
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:2721
|
||||
#: addons/web/static/src/js/view_form.js:2740
|
||||
msgid "Add: "
|
||||
msgstr ""
|
||||
|
||||
|
@ -532,22 +610,26 @@ msgstr ""
|
|||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_list.js:305
|
||||
#: addons/web/static/src/js/view_list.js:309
|
||||
#, python-format
|
||||
msgid "[%(first_record)d to %(last_record)d] of %(records_count)d"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_list.js:524
|
||||
#: addons/web/static/src/js/view_list.js:528
|
||||
msgid "Do you really want to remove these records?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_list.js:1230
|
||||
#: addons/web/static/src/js/view_list.js:1232
|
||||
msgid "Undefined"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_list.js:1327
|
||||
#: addons/web/static/src/js/view_list.js:1331
|
||||
#, python-format
|
||||
msgid "%(page)d/%(page_count)d"
|
||||
msgstr ""
|
||||
|
@ -568,7 +650,8 @@ msgid "Tree"
|
|||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:565 addons/web/static/src/xml/base.xml:480
|
||||
#: addons/web/static/src/js/views.js:565
|
||||
#: addons/web/static/src/xml/base.xml:480
|
||||
msgid "Fields View Get"
|
||||
msgstr ""
|
||||
|
||||
|
@ -585,7 +668,8 @@ msgid "Model %s fields"
|
|||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:610 addons/web/static/src/xml/base.xml:482
|
||||
#: addons/web/static/src/js/views.js:610
|
||||
#: addons/web/static/src/xml/base.xml:482
|
||||
msgid "Manage Views"
|
||||
msgstr ""
|
||||
|
||||
|
@ -652,12 +736,14 @@ msgid "Translations"
|
|||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
#: addons/web/static/src/xml/base.xml:44
|
||||
#: addons/web/static/src/xml/base.xml:315
|
||||
msgid "Powered by"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
#: addons/web/static/src/xml/base.xml:44
|
||||
#: addons/web/static/src/xml/base.xml:315
|
||||
#: addons/web/static/src/xml/base.xml:1813
|
||||
msgid "OpenERP"
|
||||
msgstr ""
|
||||
|
@ -673,12 +759,14 @@ msgid "CREATE DATABASE"
|
|||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211
|
||||
#: addons/web/static/src/xml/base.xml:68
|
||||
#: addons/web/static/src/xml/base.xml:211
|
||||
msgid "Master password:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:72 addons/web/static/src/xml/base.xml:191
|
||||
#: addons/web/static/src/xml/base.xml:72
|
||||
#: addons/web/static/src/xml/base.xml:191
|
||||
msgid "New database name:"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-15 14:37+0000\n"
|
||||
"PO-Revision-Date: 2012-02-16 10:56+0000\n"
|
||||
"Last-Translator: Erwin <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -534,7 +534,7 @@ msgstr "Onbeperkt"
|
|||
#: addons/web/static/src/js/view_list.js:305
|
||||
#, python-format
|
||||
msgid "[%(first_record)d to %(last_record)d] of %(records_count)d"
|
||||
msgstr "[%(first_record)d tot %(last_record)d] van %(records_count)d"
|
||||
msgstr "[%(first_record)d t/m %(last_record)d] van %(records_count)d"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_list.js:524
|
||||
|
@ -592,7 +592,7 @@ msgstr "Weergaven beheren"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:611
|
||||
msgid "Could not find current view declaration"
|
||||
msgstr ""
|
||||
msgstr "Kan huidige weergave declaratie niet vinden"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:805
|
||||
|
@ -989,7 +989,7 @@ msgstr "Alle gebruikers"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:851
|
||||
msgid "Unhandled widget"
|
||||
msgstr ""
|
||||
msgstr "Niet-verwerkte widget"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:900
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 07:26+0000\n"
|
||||
"PO-Revision-Date: 2012-02-20 07:27+0000\n"
|
||||
"Last-Translator: Aleksei Motsik <Unknown>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-21 06:10+0000\n"
|
||||
"X-Generator: Launchpad (build 14838)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -150,12 +150,12 @@ msgstr "Экспортировать в файл"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:125
|
||||
msgid "Please enter save field list name"
|
||||
msgstr ""
|
||||
msgstr "Введите имя для сохраняемого списка полей"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:360
|
||||
msgid "Please select fields to save export list..."
|
||||
msgstr ""
|
||||
msgstr "Выберите поля для сохранения в списке экспорта..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/data_export.js:373
|
||||
|
@ -192,7 +192,7 @@ msgstr "Загрузка \"%s\""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:191
|
||||
msgid "Filter disabled due to invalid syntax"
|
||||
msgstr ""
|
||||
msgstr "Фильтр отключен так-как имеет неверный синтаксис"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:237
|
||||
|
@ -219,7 +219,7 @@ msgstr "Ошибка поиска"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:415
|
||||
msgid "triggered from search view"
|
||||
msgstr ""
|
||||
msgstr "вызвано из поиска"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:503
|
||||
|
@ -382,12 +382,12 @@ msgstr "Редактор Вида %d - %s"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:367
|
||||
msgid "Inherited View"
|
||||
msgstr ""
|
||||
msgstr "Унаследованный Вид"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:371
|
||||
msgid "Do you really wants to create an inherited view here?"
|
||||
msgstr ""
|
||||
msgstr "Вы действительно хотите создать наследующий вид?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:381
|
||||
|
@ -426,7 +426,7 @@ msgstr "Настроить"
|
|||
#: addons/web/static/src/js/view_form.js:123
|
||||
#: addons/web/static/src/js/view_form.js:686
|
||||
msgid "Set Default"
|
||||
msgstr ""
|
||||
msgstr "Установить по умолчанию"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:469
|
||||
|
@ -437,7 +437,7 @@ msgstr "Внимание. Эта запись была изменена. Ваш
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:693
|
||||
msgid "Save default"
|
||||
msgstr ""
|
||||
msgstr "Сохранить как По Умолчанию"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_form.js:754
|
||||
|
@ -551,7 +551,7 @@ msgstr "Не определено"
|
|||
#: addons/web/static/src/js/view_list.js:1327
|
||||
#, python-format
|
||||
msgid "%(page)d/%(page_count)d"
|
||||
msgstr ""
|
||||
msgstr "%(page)d/%(page_count)d"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_page.js:8
|
||||
|
@ -571,7 +571,7 @@ msgstr "Дерево"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:565 addons/web/static/src/xml/base.xml:480
|
||||
msgid "Fields View Get"
|
||||
msgstr ""
|
||||
msgstr "Получить Поля Просмотра"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/views.js:573
|
||||
|
@ -843,7 +843,7 @@ msgstr "Вернутся к Авторизации"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:353
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
msgstr "Домой"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:363
|
||||
|
@ -853,12 +853,12 @@ msgstr "ВЫЙТИ"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:388
|
||||
msgid "Fold menu"
|
||||
msgstr ""
|
||||
msgstr "Свернуть меню"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:389
|
||||
msgid "Unfold menu"
|
||||
msgstr ""
|
||||
msgstr "Развернуть меню"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:454
|
||||
|
@ -873,7 +873,7 @@ msgstr "Отключить все подсказки"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:463
|
||||
msgid "Add / Remove Shortcut..."
|
||||
msgstr ""
|
||||
msgstr "Добавить / Удалить ярлык..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:471
|
||||
|
@ -883,32 +883,32 @@ msgstr "Больше..."
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:477
|
||||
msgid "Debug View#"
|
||||
msgstr ""
|
||||
msgstr "Debug View#"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:478
|
||||
msgid "View Log (perm_read)"
|
||||
msgstr ""
|
||||
msgstr "Просмотр Лога (доступ на чтение)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:479
|
||||
msgid "View Fields"
|
||||
msgstr ""
|
||||
msgstr "Просмотр Полей"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:483
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
msgstr "Вид"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:484
|
||||
msgid "Edit SearchView"
|
||||
msgstr ""
|
||||
msgstr "Изменить Вид Поиска"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:485
|
||||
msgid "Edit Action"
|
||||
msgstr ""
|
||||
msgstr "Изменить действие"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:486
|
||||
|
@ -918,191 +918,191 @@ msgstr "Редактировать Процесс"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:491
|
||||
msgid "ID:"
|
||||
msgstr ""
|
||||
msgstr "ID:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:494
|
||||
msgid "XML ID:"
|
||||
msgstr ""
|
||||
msgstr "XML ID:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:497
|
||||
msgid "Creation User:"
|
||||
msgstr ""
|
||||
msgstr "Создатель:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:500
|
||||
msgid "Creation Date:"
|
||||
msgstr ""
|
||||
msgstr "Дата Создания:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:503
|
||||
msgid "Latest Modification by:"
|
||||
msgstr ""
|
||||
msgstr "Изменялся:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:506
|
||||
msgid "Latest Modification Date:"
|
||||
msgstr ""
|
||||
msgstr "Дата Изменения:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:542
|
||||
msgid "Field"
|
||||
msgstr ""
|
||||
msgstr "Поле"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:632
|
||||
#: addons/web/static/src/xml/base.xml:758
|
||||
#: addons/web/static/src/xml/base.xml:1708
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
msgstr "Удалить"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:757
|
||||
msgid "Duplicate"
|
||||
msgstr ""
|
||||
msgstr "Клонировать"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:775
|
||||
msgid "Add attachment"
|
||||
msgstr ""
|
||||
msgstr "Добавить вложение"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:801
|
||||
msgid "Default:"
|
||||
msgstr ""
|
||||
msgstr "По умолчанию:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:818
|
||||
msgid "Condition:"
|
||||
msgstr ""
|
||||
msgstr "Условие:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:837
|
||||
msgid "Only you"
|
||||
msgstr ""
|
||||
msgstr "Только Вы"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:844
|
||||
msgid "All users"
|
||||
msgstr ""
|
||||
msgstr "Все пользователи"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:851
|
||||
msgid "Unhandled widget"
|
||||
msgstr ""
|
||||
msgstr "Неподдерживаемый виджет"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:900
|
||||
msgid "Notebook Page \""
|
||||
msgstr ""
|
||||
msgstr "Страница Блокнота \""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:905
|
||||
#: addons/web/static/src/xml/base.xml:964
|
||||
msgid "Modifiers:"
|
||||
msgstr ""
|
||||
msgstr "Модификаторы:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:931
|
||||
msgid "(nolabel)"
|
||||
msgstr ""
|
||||
msgstr "(без метки)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:936
|
||||
msgid "Field:"
|
||||
msgstr ""
|
||||
msgstr "Поле:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:940
|
||||
msgid "Object:"
|
||||
msgstr ""
|
||||
msgstr "Объект:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:944
|
||||
msgid "Type:"
|
||||
msgstr ""
|
||||
msgstr "Тип:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:948
|
||||
msgid "Widget:"
|
||||
msgstr ""
|
||||
msgstr "Виджет:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:952
|
||||
msgid "Size:"
|
||||
msgstr ""
|
||||
msgstr "Размер:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:956
|
||||
msgid "Context:"
|
||||
msgstr ""
|
||||
msgstr "Контекст:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:960
|
||||
msgid "Domain:"
|
||||
msgstr ""
|
||||
msgstr "Домен:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:968
|
||||
msgid "Change default:"
|
||||
msgstr ""
|
||||
msgstr "Изменить по умолчанию:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:972
|
||||
msgid "On change:"
|
||||
msgstr ""
|
||||
msgstr "При изменении:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:976
|
||||
msgid "Relation:"
|
||||
msgstr ""
|
||||
msgstr "Отношение:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:980
|
||||
msgid "Selection:"
|
||||
msgstr ""
|
||||
msgstr "Выбор:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1020
|
||||
msgid "Send an e-mail with your default e-mail client"
|
||||
msgstr ""
|
||||
msgstr "Отправлять имейл вашим почтовым клиентом"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1034
|
||||
msgid "Open this resource"
|
||||
msgstr ""
|
||||
msgstr "Открыть этот ресурс"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1056
|
||||
msgid "Select date"
|
||||
msgstr ""
|
||||
msgstr "Выбрать дату"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1090
|
||||
msgid "Open..."
|
||||
msgstr ""
|
||||
msgstr "Открыть..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1091
|
||||
msgid "Create..."
|
||||
msgstr ""
|
||||
msgstr "Создать…"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1092
|
||||
msgid "Search..."
|
||||
msgstr ""
|
||||
msgstr "Поиск…"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1095
|
||||
msgid "..."
|
||||
msgstr ""
|
||||
msgstr "..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1155
|
||||
#: addons/web/static/src/xml/base.xml:1198
|
||||
msgid "Set Image"
|
||||
msgstr ""
|
||||
msgstr "Назначить изображение"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1163
|
||||
|
@ -1110,150 +1110,150 @@ msgstr ""
|
|||
#: addons/web/static/src/xml/base.xml:1215
|
||||
#: addons/web/static/src/xml/base.xml:1272
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
msgstr "Очистить"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1172
|
||||
#: addons/web/static/src/xml/base.xml:1223
|
||||
msgid "Uploading ..."
|
||||
msgstr ""
|
||||
msgstr "Загружаю ..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1200
|
||||
#: addons/web/static/src/xml/base.xml:1495
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
msgstr "Выбор"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1207
|
||||
#: addons/web/static/src/xml/base.xml:1209
|
||||
msgid "Save As"
|
||||
msgstr ""
|
||||
msgstr "Сохранить как"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1238
|
||||
msgid "Button"
|
||||
msgstr ""
|
||||
msgstr "Кнопка"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1241
|
||||
msgid "(no string)"
|
||||
msgstr ""
|
||||
msgstr "(нет строки)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1248
|
||||
msgid "Special:"
|
||||
msgstr ""
|
||||
msgstr "Специальное:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1253
|
||||
msgid "Button Type:"
|
||||
msgstr ""
|
||||
msgstr "Тип Кнопки:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1257
|
||||
msgid "Method:"
|
||||
msgstr ""
|
||||
msgstr "Метод:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1261
|
||||
msgid "Action ID:"
|
||||
msgstr ""
|
||||
msgstr "ID Действия:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1271
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
msgstr "Поиск"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1279
|
||||
msgid "Filters"
|
||||
msgstr ""
|
||||
msgstr "Фильтры"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1280
|
||||
msgid "-- Filters --"
|
||||
msgstr ""
|
||||
msgstr "-- Фильтры --"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1289
|
||||
msgid "-- Actions --"
|
||||
msgstr ""
|
||||
msgstr "-- Действия --"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1290
|
||||
msgid "Add Advanced Filter"
|
||||
msgstr ""
|
||||
msgstr "Добавить Расширенный Фильтр"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1291
|
||||
msgid "Save Filter"
|
||||
msgstr ""
|
||||
msgstr "Сохранить фильтр"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1293
|
||||
msgid "Manage Filters"
|
||||
msgstr ""
|
||||
msgstr "Управление фильтрами"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1298
|
||||
msgid "Filter Name:"
|
||||
msgstr ""
|
||||
msgstr "Название Фильтра:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1300
|
||||
msgid "(Any existing filter with the same name will be replaced)"
|
||||
msgstr ""
|
||||
msgstr "(Существующий фильтр с таким же именени будет замещен)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1305
|
||||
msgid "Select Dashboard to add this filter to:"
|
||||
msgstr ""
|
||||
msgstr "Выберите Дашбоард, к которому добавить этот фильтр:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1309
|
||||
msgid "Title of new Dashboard item:"
|
||||
msgstr ""
|
||||
msgstr "Заголовок нового Дашбоарда:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1416
|
||||
msgid "Advanced Filters"
|
||||
msgstr ""
|
||||
msgstr "Расширенные Фильтры"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1426
|
||||
msgid "Any of the following conditions must match"
|
||||
msgstr ""
|
||||
msgstr "Одно из следующих условий должно соответствовать"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1427
|
||||
msgid "All the following conditions must match"
|
||||
msgstr ""
|
||||
msgstr "Все следующие условия должны соответствовать"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1428
|
||||
msgid "None of the following conditions must match"
|
||||
msgstr ""
|
||||
msgstr "Ни одно из следующих условий не должно соответствовать"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1435
|
||||
msgid "Add condition"
|
||||
msgstr ""
|
||||
msgstr "Добавить условие"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1436
|
||||
msgid "and"
|
||||
msgstr ""
|
||||
msgstr "и"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1503
|
||||
msgid "Save & New"
|
||||
msgstr ""
|
||||
msgstr "Сохранить и Создать"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1504
|
||||
msgid "Save & Close"
|
||||
msgstr ""
|
||||
msgstr "Сохранить и Закрыть"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1611
|
||||
|
@ -1263,81 +1263,84 @@ msgid ""
|
|||
" You can export all data or only the fields that can be "
|
||||
"reimported after modification."
|
||||
msgstr ""
|
||||
"Этот мастер экспортирует все найденные данные в CSV файл.\n"
|
||||
" Вы можете экспортровать все данные либо только те поля которые "
|
||||
"могут быть в последствии импортированны."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1618
|
||||
msgid "Export Type:"
|
||||
msgstr ""
|
||||
msgstr "Тип экспорта:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1620
|
||||
msgid "Import Compatible Export"
|
||||
msgstr ""
|
||||
msgstr "Импорт совместимого Экспорта"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1621
|
||||
msgid "Export all Data"
|
||||
msgstr ""
|
||||
msgstr "Экспортировать все данные"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1624
|
||||
msgid "Export Formats"
|
||||
msgstr ""
|
||||
msgstr "Форматы Экспорта"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1630
|
||||
msgid "Available fields"
|
||||
msgstr ""
|
||||
msgstr "Доступные поля"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1632
|
||||
msgid "Fields to export"
|
||||
msgstr ""
|
||||
msgstr "Поля для экспорта"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1634
|
||||
msgid "Save fields list"
|
||||
msgstr ""
|
||||
msgstr "Сохранить список полей"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1648
|
||||
msgid "Remove All"
|
||||
msgstr ""
|
||||
msgstr "Удалить все"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1660
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
msgstr "Название"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1693
|
||||
msgid "Save as:"
|
||||
msgstr ""
|
||||
msgstr "Сохранить как:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1700
|
||||
msgid "Saved exports:"
|
||||
msgstr ""
|
||||
msgstr "Сохраненые экспорты:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1714
|
||||
msgid "Old Password:"
|
||||
msgstr ""
|
||||
msgstr "Старый пароль:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1719
|
||||
msgid "New Password:"
|
||||
msgstr ""
|
||||
msgstr "Новый пароль:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1724
|
||||
msgid "Confirm Password:"
|
||||
msgstr ""
|
||||
msgstr "Пароль ещё раз:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1742
|
||||
msgid "1. Import a .CSV file"
|
||||
msgstr ""
|
||||
msgstr "1. Импорт из .CSV файла"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1743
|
||||
|
@ -1345,56 +1348,59 @@ msgid ""
|
|||
"Select a .CSV file to import. If you need a sample of file to import,\n"
|
||||
" you should use the export tool with the \"Import Compatible\" option."
|
||||
msgstr ""
|
||||
"Укажите .CSV файл для импорта. Если вам нужен пример такого файла,\n"
|
||||
" воспользуйтесь инструментом экспорта с опцией \"Совместимость для "
|
||||
"Импорта\"."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1747
|
||||
msgid "CSV File:"
|
||||
msgstr ""
|
||||
msgstr "Файл CSV:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1750
|
||||
msgid "2. Check your file format"
|
||||
msgstr ""
|
||||
msgstr "2. Проверьте формат файла"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1753
|
||||
msgid "Import Options"
|
||||
msgstr ""
|
||||
msgstr "Параметры импорта"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1757
|
||||
msgid "Does your file have titles?"
|
||||
msgstr ""
|
||||
msgstr "В файле есть заголовки (строка с названиями колонок)?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1763
|
||||
msgid "Separator:"
|
||||
msgstr ""
|
||||
msgstr "Разделитель:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1765
|
||||
msgid "Delimiter:"
|
||||
msgstr ""
|
||||
msgstr "Разделитель:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1769
|
||||
msgid "Encoding:"
|
||||
msgstr ""
|
||||
msgstr "Кодировка:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1772
|
||||
msgid "UTF-8"
|
||||
msgstr ""
|
||||
msgstr "UTF-8"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1773
|
||||
msgid "Latin 1"
|
||||
msgstr ""
|
||||
msgstr "Latin 1"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1776
|
||||
msgid "Lines to skip"
|
||||
msgstr ""
|
||||
msgstr "Пропустить строки"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1776
|
||||
|
@ -1402,58 +1408,60 @@ msgid ""
|
|||
"For use if CSV files have titles on multiple lines, skips more than a single "
|
||||
"line during import"
|
||||
msgstr ""
|
||||
"Применимо если в CSV файле заголовки расположены в нескольких строках и из "
|
||||
"необходимо пропустить"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1803
|
||||
msgid "The import failed due to:"
|
||||
msgstr ""
|
||||
msgstr "Импорт не выполнен по причине:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1805
|
||||
msgid "Here is a preview of the file we could not import:"
|
||||
msgstr ""
|
||||
msgstr "Предпросмотр файла, который система не смогла импортировать:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1812
|
||||
msgid "Activate the developper mode"
|
||||
msgstr ""
|
||||
msgstr "Активировать режим разработчика"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1814
|
||||
msgid "Version"
|
||||
msgstr ""
|
||||
msgstr "Версия"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1815
|
||||
msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved."
|
||||
msgstr ""
|
||||
msgstr "Авторское право © 2004-СЕГОДНЯ OpenERP SA. Все Права Защищены."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1816
|
||||
msgid "OpenERP is a trademark of the"
|
||||
msgstr ""
|
||||
msgstr "OpenERP является торговым знаком"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1817
|
||||
msgid "OpenERP SA Company"
|
||||
msgstr ""
|
||||
msgstr "OpenERP SA Company"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1819
|
||||
msgid "Licenced under the terms of"
|
||||
msgstr ""
|
||||
msgstr "Лицензированно по условиям"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1820
|
||||
msgid "GNU Affero General Public License"
|
||||
msgstr ""
|
||||
msgstr "GNU Affero General Public License"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1822
|
||||
msgid "For more information visit"
|
||||
msgstr ""
|
||||
msgstr "Для получения подробной информации посетите"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:1823
|
||||
msgid "OpenERP.com"
|
||||
msgstr ""
|
||||
msgstr "OpenERP.com"
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-09 20:50+0000\n"
|
||||
"PO-Revision-Date: 2012-02-24 11:29+0000\n"
|
||||
"Last-Translator: Ahmet Altınışık <Unknown>\n"
|
||||
"Language-Team: Turkish <tr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-25 05:30+0000\n"
|
||||
"X-Generator: Launchpad (build 14860)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -192,7 +192,7 @@ msgstr "Karşıdan yükle \"%s\""
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:191
|
||||
msgid "Filter disabled due to invalid syntax"
|
||||
msgstr ""
|
||||
msgstr "Geçersiz sözdizimi nedeniyle filtre engellendi"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/search.js:237
|
||||
|
@ -381,12 +381,12 @@ msgstr "Editörü göster %d - %s"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:367
|
||||
msgid "Inherited View"
|
||||
msgstr ""
|
||||
msgstr "Devralınan Görünüm"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:371
|
||||
msgid "Do you really wants to create an inherited view here?"
|
||||
msgstr ""
|
||||
msgstr "Devralınmış görünüm oluşturmak istediğinden emin misin?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/view_editor.js:381
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-15 13:19+0000\n"
|
||||
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
|
||||
"PO-Revision-Date: 2012-02-17 07:29+0000\n"
|
||||
"Last-Translator: Jeff Wang <wjfonhand@hotmail.com>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-18 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:172
|
||||
|
@ -654,7 +654,7 @@ msgstr "翻译"
|
|||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
msgid "Powered by"
|
||||
msgstr "动力来自"
|
||||
msgstr "选择自由,选择"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/*
|
||||
Fork of some unlicensed library found somewhere, don't hesitate to
|
||||
patch it directly.
|
||||
*/
|
||||
(function($) {
|
||||
var menu,shadow,trigger,content,hash,currentTarget;
|
||||
var defaults= {
|
||||
|
@ -30,7 +34,7 @@
|
|||
};
|
||||
$.fn.contextMenu= function(id,options) {
|
||||
if(!menu) {
|
||||
menu=$('<div id="jqContextMenu"></div>').hide().css({
|
||||
menu=$('<div id="jqContextMenu" class="openerp"></div>').hide().css({
|
||||
position:'absolute',
|
||||
zIndex:'2000'
|
||||
}).appendTo('body').bind('click', function(e) {
|
||||
|
|
6044
addons/web_mobile/static/openerp/mockup/jquery-1.5.1.js → addons/web/static/lib/jquery/jquery-1.7.2b1.js
Executable file → Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
Copyright (c) 2011, OpenERP S.A.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
nova = (function() {
|
||||
var lib = {};
|
||||
lib.internal = {};
|
||||
|
||||
/*
|
||||
* (Almost) unmodified John Resig's inheritance
|
||||
*/
|
||||
/*
|
||||
* Simple JavaScript Inheritance By John Resig http://ejohn.org/ MIT
|
||||
* Licensed.
|
||||
*/
|
||||
// Inspired by base2 and Prototype
|
||||
(function() {
|
||||
var initializing = false, fnTest = /xyz/.test(function() {
|
||||
xyz;
|
||||
}) ? /\b_super\b/ : /.*/;
|
||||
// The base Class implementation (does nothing)
|
||||
this.Class = function() {
|
||||
};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
this.Class.extend = function(prop) {
|
||||
var _super = this.prototype;
|
||||
|
||||
// Instantiate a web class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] = typeof prop[name] == "function" &&
|
||||
typeof _super[name] == "function" &&
|
||||
fnTest.test(prop[name]) ?
|
||||
(function(name, fn) {
|
||||
return function() {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same
|
||||
// method but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so
|
||||
// we remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name]) :
|
||||
prop[name];
|
||||
}
|
||||
|
||||
// The dummy class constructor
|
||||
function Class() {
|
||||
// All construction is actually done in the init method
|
||||
if (!initializing && this.init) {
|
||||
var ret = this.init.apply(this, arguments);
|
||||
if (ret) { return ret; }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Class.include = function (properties) {
|
||||
for (var name in properties) {
|
||||
if (typeof properties[name] !== 'function'
|
||||
|| !fnTest.test(properties[name])) {
|
||||
prototype[name] = properties[name];
|
||||
} else if (typeof prototype[name] === 'function'
|
||||
&& prototype.hasOwnProperty(name)) {
|
||||
prototype[name] = (function (name, fn, previous) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
this._super = previous;
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
return ret;
|
||||
}
|
||||
})(name, properties[name], prototype[name]);
|
||||
} else if (typeof _super[name] === 'function') {
|
||||
prototype[name] = (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
this._super = _super[name];
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
return ret;
|
||||
}
|
||||
})(name, properties[name]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Populate our constructed prototype object
|
||||
Class.prototype = prototype;
|
||||
|
||||
// Enforce the constructor to be what we expect
|
||||
Class.constructor = Class;
|
||||
|
||||
// And make this class extendable
|
||||
Class.extend = arguments.callee;
|
||||
|
||||
return Class;
|
||||
};
|
||||
}).call(lib);
|
||||
// end of John Resig's code
|
||||
|
||||
lib.DestroyableMixin = {
|
||||
init: function() {
|
||||
this.__destroyableDestroyed = false;
|
||||
},
|
||||
isDestroyed : function() {
|
||||
return this.__destroyableDestroyed;
|
||||
},
|
||||
destroy : function() {
|
||||
this.__destroyableDestroyed = true;
|
||||
}
|
||||
};
|
||||
|
||||
lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, {
|
||||
__parentedMixin : true,
|
||||
init: function() {
|
||||
lib.DestroyableMixin.init.call(this);
|
||||
this.__parentedChildren = [];
|
||||
this.__parentedParent = null;
|
||||
},
|
||||
setParent : function(parent) {
|
||||
if (this.getParent()) {
|
||||
if (this.getParent().__parentedMixin) {
|
||||
this.getParent().__parentedChildren = _.without(this
|
||||
.getParent().getChildren(), this);
|
||||
}
|
||||
}
|
||||
this.__parentedParent = parent;
|
||||
if (parent && parent.__parentedMixin) {
|
||||
parent.__parentedChildren.push(this);
|
||||
}
|
||||
},
|
||||
getParent : function() {
|
||||
return this.__parentedParent;
|
||||
},
|
||||
getChildren : function() {
|
||||
return _.clone(this.__parentedChildren);
|
||||
},
|
||||
destroy : function() {
|
||||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
this.setParent(undefined);
|
||||
lib.DestroyableMixin.destroy.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Yes, we steal Backbone's events :)
|
||||
*
|
||||
* This class just handle the dispatching of events, it is not meant to be extended,
|
||||
* nor used directly. All integration with parenting and automatic unregistration of
|
||||
* events is done in EventDispatcherMixin.
|
||||
*/
|
||||
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://backbonejs.org
|
||||
lib.internal.Events = lib.Class.extend({
|
||||
|
||||
on : function(events, callback, context) {
|
||||
var ev;
|
||||
events = events.split(/\s+/);
|
||||
var calls = this._callbacks || (this._callbacks = {});
|
||||
while (ev = events.shift()) {
|
||||
var list = calls[ev] || (calls[ev] = {});
|
||||
var tail = list.tail || (list.tail = list.next = {});
|
||||
tail.callback = callback;
|
||||
tail.context = context;
|
||||
list.tail = tail.next = {};
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
off : function(events, callback, context) {
|
||||
var ev, calls, node;
|
||||
if (!events) {
|
||||
delete this._callbacks;
|
||||
} else if (calls = this._callbacks) {
|
||||
events = events.split(/\s+/);
|
||||
while (ev = events.shift()) {
|
||||
node = calls[ev];
|
||||
delete calls[ev];
|
||||
if (!callback || !node)
|
||||
continue;
|
||||
while ((node = node.next) && node.next) {
|
||||
if (node.callback === callback
|
||||
&& (!context || node.context === context))
|
||||
continue;
|
||||
this.on(ev, node.callback, node.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
trigger : function(events) {
|
||||
var event, node, calls, tail, args, all, rest;
|
||||
if (!(calls = this._callbacks))
|
||||
return this;
|
||||
all = calls['all'];
|
||||
(events = events.split(/\s+/)).push(null);
|
||||
// Save references to the current heads & tails.
|
||||
while (event = events.shift()) {
|
||||
if (all)
|
||||
events.push({
|
||||
next : all.next,
|
||||
tail : all.tail,
|
||||
event : event
|
||||
});
|
||||
if (!(node = calls[event]))
|
||||
continue;
|
||||
events.push({
|
||||
next : node.next,
|
||||
tail : node.tail
|
||||
});
|
||||
}
|
||||
rest = Array.prototype.slice.call(arguments, 1);
|
||||
while (node = events.pop()) {
|
||||
tail = node.tail;
|
||||
args = node.event ? [ node.event ].concat(rest) : rest;
|
||||
while ((node = node.next) !== tail) {
|
||||
node.callback.apply(node.context || this, args);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
// end of Backbone's events class
|
||||
|
||||
lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, {
|
||||
__eventDispatcherMixin: true,
|
||||
init: function() {
|
||||
lib.ParentedMixin.init.call(this);
|
||||
this.__edispatcherEvents = new lib.internal.Events();
|
||||
this.__edispatcherRegisteredEvents = [];
|
||||
},
|
||||
on: function(events, dest, func) {
|
||||
var self = this;
|
||||
events = events.split(/\s+/);
|
||||
_.each(events, function(eventName) {
|
||||
self.__edispatcherEvents.on(eventName, func, dest);
|
||||
if (dest && dest.__eventDispatcherMixin) {
|
||||
dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
off: function(events, dest, func) {
|
||||
var self = this;
|
||||
events = events.split(/\s+/);
|
||||
_.each(events, function(eventName) {
|
||||
self.__edispatcherEvents.off(eventName, func, dest);
|
||||
if (dest && dest.__eventDispatcherMixin) {
|
||||
dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
|
||||
return !(el.name === eventName && el.func === func && el.source === self);
|
||||
});
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
trigger: function(events) {
|
||||
this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
|
||||
return this;
|
||||
},
|
||||
destroy: function() {
|
||||
var self = this;
|
||||
_.each(this.__edispatcherRegisteredEvents, function(event) {
|
||||
event.source.__edispatcherEvents.off(event.name, event.func, self);
|
||||
});
|
||||
this.__edispatcherRegisteredEvents = [];
|
||||
this.__edispatcherEvents.off();
|
||||
lib.ParentedMixin.destroy.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, {
|
||||
init: function() {
|
||||
lib.EventDispatcherMixin.init.call(this);
|
||||
this.__getterSetterInternalMap = {};
|
||||
},
|
||||
set: function(map) {
|
||||
var self = this;
|
||||
var changed = false;
|
||||
_.each(map, function(val, key) {
|
||||
var tmp = self.__getterSetterInternalMap[key];
|
||||
if (tmp === val)
|
||||
return;
|
||||
changed = true;
|
||||
self.__getterSetterInternalMap[key] = val;
|
||||
self.trigger("change:" + key, self, {
|
||||
oldValue: tmp,
|
||||
newValue: val
|
||||
});
|
||||
});
|
||||
if (changed)
|
||||
self.trigger("change", self);
|
||||
},
|
||||
get: function(key) {
|
||||
return this.__getterSetterInternalMap[key];
|
||||
}
|
||||
});
|
||||
|
||||
lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, {
|
||||
/**
|
||||
* Tag name when creating a default $element.
|
||||
* @type string
|
||||
*/
|
||||
tagName: 'div',
|
||||
/**
|
||||
* Constructs the widget and sets its parent if a parent is given.
|
||||
*
|
||||
* @constructs openerp.web.Widget
|
||||
* @extends openerp.web.CallbackEnabled
|
||||
*
|
||||
* @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
|
||||
* When that widget is destroyed by calling destroy(), the current instance will be
|
||||
* destroyed too. Can be null.
|
||||
* @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
|
||||
* to bind the current Widget to an already existing part of the DOM, which is not compatible
|
||||
* with the DOM insertion methods provided by the current implementation of Widget. So
|
||||
* for new components this argument should not be provided any more.
|
||||
*/
|
||||
init: function(parent) {
|
||||
lib.GetterSetterMixin.init.call(this);
|
||||
this.$element = $(document.createElement(this.tagName));
|
||||
|
||||
this.setParent(parent);
|
||||
},
|
||||
/**
|
||||
* Destroys the current widget, also destroys all its children before destroying itself.
|
||||
*/
|
||||
destroy: function() {
|
||||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
if(this.$element != null) {
|
||||
this.$element.remove();
|
||||
}
|
||||
lib.GetterSetterMixin.destroy.call(this);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and appends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
appendTo: function(target) {
|
||||
var self = this;
|
||||
return this.__widgetRenderAndInsert(function(t) {
|
||||
self.$element.appendTo(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and prepends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
prependTo: function(target) {
|
||||
var self = this;
|
||||
return this.__widgetRenderAndInsert(function(t) {
|
||||
self.$element.prependTo(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it after to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertAfter: function(target) {
|
||||
var self = this;
|
||||
return this.__widgetRenderAndInsert(function(t) {
|
||||
self.$element.insertAfter(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it before to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertBefore: function(target) {
|
||||
var self = this;
|
||||
return this.__widgetRenderAndInsert(function(t) {
|
||||
self.$element.insertBefore(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and replaces the given jQuery object.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
replace: function(target) {
|
||||
return this.__widgetRenderAndInsert(_.bind(function(t) {
|
||||
this.$element.replaceAll(t);
|
||||
}, this), target);
|
||||
},
|
||||
__widgetRenderAndInsert: function(insertion, target) {
|
||||
this.renderElement();
|
||||
insertion(target);
|
||||
return this.start();
|
||||
},
|
||||
/**
|
||||
* This is the method to implement to render the Widget.
|
||||
*/
|
||||
renderElement: function() {},
|
||||
/**
|
||||
* Method called after rendering. Mostly used to bind actions, perform asynchronous
|
||||
* calls, etc...
|
||||
*
|
||||
* By convention, the method should return a promise to inform the caller when
|
||||
* this widget has been initialized.
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
start: function() {}
|
||||
}));
|
||||
|
||||
return lib;
|
||||
})();
|
3394
addons/web_mobile/static/openerp/dynamics/jquery-1.5.1.js → addons/web/static/lib/novajs/test/jquery-1.6.4.js
vendored
Executable file → Normal file
|
@ -0,0 +1,226 @@
|
|||
/**
|
||||
* QUnit v1.2.0 - A JavaScript Unit Testing Framework
|
||||
*
|
||||
* http://docs.jquery.com/QUnit
|
||||
*
|
||||
* Copyright (c) 2011 John Resig, Jörn Zaefferer
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt)
|
||||
* or GPL (GPL-LICENSE.txt) licenses.
|
||||
*/
|
||||
|
||||
/** Font Family and Sizes */
|
||||
|
||||
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
|
||||
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
|
||||
#qunit-tests { font-size: smaller; }
|
||||
|
||||
|
||||
/** Resets */
|
||||
|
||||
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
/** Header */
|
||||
|
||||
#qunit-header {
|
||||
padding: 0.5em 0 0.5em 1em;
|
||||
|
||||
color: #8699a4;
|
||||
background-color: #0d3349;
|
||||
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
font-weight: normal;
|
||||
|
||||
border-radius: 15px 15px 0 0;
|
||||
-moz-border-radius: 15px 15px 0 0;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
-webkit-border-top-left-radius: 15px;
|
||||
}
|
||||
|
||||
#qunit-header a {
|
||||
text-decoration: none;
|
||||
color: #c2ccd1;
|
||||
}
|
||||
|
||||
#qunit-header a:hover,
|
||||
#qunit-header a:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#qunit-banner {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
#qunit-testrunner-toolbar {
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
color: #5E740B;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#qunit-userAgent {
|
||||
padding: 0.5em 0 0.5em 2.5em;
|
||||
background-color: #2b81af;
|
||||
color: #fff;
|
||||
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
|
||||
}
|
||||
|
||||
|
||||
/** Tests: Pass/Fail */
|
||||
|
||||
#qunit-tests {
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
#qunit-tests li {
|
||||
padding: 0.4em 0.5em 0.4em 2.5em;
|
||||
border-bottom: 1px solid #fff;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#qunit-tests li strong {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#qunit-tests li a {
|
||||
padding: 0.5em;
|
||||
color: #c2ccd1;
|
||||
text-decoration: none;
|
||||
}
|
||||
#qunit-tests li a:hover,
|
||||
#qunit-tests li a:focus {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#qunit-tests ol {
|
||||
margin-top: 0.5em;
|
||||
padding: 0.5em;
|
||||
|
||||
background-color: #fff;
|
||||
|
||||
border-radius: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
|
||||
box-shadow: inset 0px 2px 13px #999;
|
||||
-moz-box-shadow: inset 0px 2px 13px #999;
|
||||
-webkit-box-shadow: inset 0px 2px 13px #999;
|
||||
}
|
||||
|
||||
#qunit-tests table {
|
||||
border-collapse: collapse;
|
||||
margin-top: .2em;
|
||||
}
|
||||
|
||||
#qunit-tests th {
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
padding: 0 .5em 0 0;
|
||||
}
|
||||
|
||||
#qunit-tests td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#qunit-tests pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#qunit-tests del {
|
||||
background-color: #e0f2be;
|
||||
color: #374e0c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#qunit-tests ins {
|
||||
background-color: #ffcaca;
|
||||
color: #500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*** Test Counts */
|
||||
|
||||
#qunit-tests b.counts { color: black; }
|
||||
#qunit-tests b.passed { color: #5E740B; }
|
||||
#qunit-tests b.failed { color: #710909; }
|
||||
|
||||
#qunit-tests li li {
|
||||
margin: 0.5em;
|
||||
padding: 0.4em 0.5em 0.4em 0.5em;
|
||||
background-color: #fff;
|
||||
border-bottom: none;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
/*** Passing Styles */
|
||||
|
||||
#qunit-tests li li.pass {
|
||||
color: #5E740B;
|
||||
background-color: #fff;
|
||||
border-left: 26px solid #C6E746;
|
||||
}
|
||||
|
||||
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
|
||||
#qunit-tests .pass .test-name { color: #366097; }
|
||||
|
||||
#qunit-tests .pass .test-actual,
|
||||
#qunit-tests .pass .test-expected { color: #999999; }
|
||||
|
||||
#qunit-banner.qunit-pass { background-color: #C6E746; }
|
||||
|
||||
/*** Failing Styles */
|
||||
|
||||
#qunit-tests li li.fail {
|
||||
color: #710909;
|
||||
background-color: #fff;
|
||||
border-left: 26px solid #EE5757;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#qunit-tests > li:last-child {
|
||||
border-radius: 0 0 15px 15px;
|
||||
-moz-border-radius: 0 0 15px 15px;
|
||||
-webkit-border-bottom-right-radius: 15px;
|
||||
-webkit-border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
|
||||
#qunit-tests .fail .test-name,
|
||||
#qunit-tests .fail .module-name { color: #000000; }
|
||||
|
||||
#qunit-tests .fail .test-actual { color: #EE5757; }
|
||||
#qunit-tests .fail .test-expected { color: green; }
|
||||
|
||||
#qunit-banner.qunit-fail { background-color: #EE5757; }
|
||||
|
||||
|
||||
/** Result */
|
||||
|
||||
#qunit-testresult {
|
||||
padding: 0.5em 0.5em 0.5em 2.5em;
|
||||
|
||||
color: #2b81af;
|
||||
background-color: #D2E0E6;
|
||||
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
/** Fixture */
|
||||
|
||||
#qunit-fixture {
|
||||
position: absolute;
|
||||
top: -10000px;
|
||||
left: -10000px;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<link rel="stylesheet" href="qunit.css" type="text/css" media="screen" />
|
||||
<script type="text/javascript" src="qunit.js"></script>
|
||||
|
||||
<script type="text/javascript" src="jquery-1.6.4.js"></script>
|
||||
<script type="text/javascript" src="underscore.js"></script>
|
||||
<script type="text/javascript" src="../src/nova.js"></script>
|
||||
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="qunit-header">QUnit example</h1>
|
||||
<h2 id="qunit-banner"></h2>
|
||||
<div id="qunit-testrunner-toolbar"></div>
|
||||
<h2 id="qunit-userAgent"></h2>
|
||||
<ol id="qunit-tests"></ol>
|
||||
<div id="qunit-fixture">test markup, will be hidden</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
module("Class");
|
||||
|
||||
test("base", function() {
|
||||
ok(!!nova.Class, "Class does exist");
|
||||
ok(!!nova.Class.extend, "extend does exist");
|
||||
var Claz = nova.Class.extend({
|
||||
test: function() {
|
||||
return "ok";
|
||||
}
|
||||
});
|
||||
equal(new Claz().test(), "ok");
|
||||
var Claz2 = Claz.extend({
|
||||
test: function() {
|
||||
return this._super() + "2";
|
||||
}
|
||||
});
|
||||
equal(new Claz2().test(), "ok2");
|
||||
});
|
||||
|
||||
module("DestroyableMixin");
|
||||
|
||||
test("base", function() {
|
||||
var Claz = nova.Class.extend(_.extend({}, nova.DestroyableMixin, {}));
|
||||
var x = new Claz();
|
||||
equal(!!x.isDestroyed(), false);
|
||||
x.destroy();
|
||||
equal(x.isDestroyed(), true);
|
||||
});
|
||||
|
||||
module("ParentedMixin");
|
||||
|
||||
test("base", function() {
|
||||
var Claz = nova.Class.extend(_.extend({}, nova.ParentedMixin, {}));
|
||||
var x = new Claz();
|
||||
var y = new Claz();
|
||||
y.setParent(x);
|
||||
equal(y.getParent(), x);
|
||||
equal(x.getChildren()[0], y);
|
||||
x.destroy();
|
||||
equal(y.isDestroyed(), true);
|
||||
});
|
||||
|
||||
module("Events");
|
||||
|
||||
test("base", function() {
|
||||
var x = new nova.internal.Events();
|
||||
var tmp = 0;
|
||||
var fct = function() {tmp = 1;};
|
||||
x.on("test", fct);
|
||||
equal(tmp, 0);
|
||||
x.trigger("test");
|
||||
equal(tmp, 1);
|
||||
tmp = 0;
|
||||
x.off("test", fct);
|
||||
x.trigger("test");
|
||||
equal(tmp, 0);
|
||||
});
|
||||
|
||||
module("EventDispatcherMixin");
|
||||
|
||||
test("base", function() {
|
||||
var Claz = nova.Class.extend(_.extend({}, nova.EventDispatcherMixin, {}));
|
||||
var x = new Claz();
|
||||
var y = new Claz();
|
||||
var tmp = 0;
|
||||
var fct = function() {tmp = 1;};
|
||||
x.on("test", y, fct);
|
||||
equal(tmp, 0);
|
||||
x.trigger("test");
|
||||
equal(tmp, 1);
|
||||
tmp = 0;
|
||||
x.off("test", y, fct);
|
||||
x.trigger("test");
|
||||
equal(tmp, 0);
|
||||
tmp = 0;
|
||||
x.on("test", y, fct);
|
||||
y.destroy();
|
||||
x.trigger("test");
|
||||
equal(tmp, 0);
|
||||
});
|
||||
|
||||
module("GetterSetterMixin");
|
||||
|
||||
test("base", function() {
|
||||
var Claz = nova.Class.extend(_.extend({}, nova.GetterSetterMixin, {}));
|
||||
var x = new Claz();
|
||||
var y = new Claz();
|
||||
x.set({test: 1});
|
||||
equal(x.get("test"), 1);
|
||||
var tmp = 0;
|
||||
x.on("change:test", y, function(model, options) {
|
||||
tmp = 1;
|
||||
equal(options.oldValue, 1);
|
||||
equal(options.newValue, 2);
|
||||
equal(x.get("test"), 2);
|
||||
equal(model, x);
|
||||
});
|
||||
x.set({test: 2});
|
||||
equal(tmp, 1);
|
||||
});
|
||||
|
||||
test("change event only when changed", function() {
|
||||
var Claz = nova.Class.extend(_.extend({}, nova.GetterSetterMixin, {}));
|
||||
var x = new Claz();
|
||||
var exec1 = false;
|
||||
var exec2 = false;
|
||||
x.on("change:test", null, function() {exec1 = true;});
|
||||
x.on("change", null, function() {exec2 = true;});
|
||||
x.set({"test": 3});
|
||||
equal(exec1, true);
|
||||
equal(exec2, true);
|
||||
exec1 = false;
|
||||
exec2 = false;
|
||||
x.set({"test": 3});
|
||||
equal(exec1, false);
|
||||
equal(exec2, false);
|
||||
});
|
||||
|
||||
module("Widget");
|
||||
|
||||
test("base", function() {
|
||||
var Claz = nova.Widget.extend({
|
||||
renderElement: function() {
|
||||
this.$element.attr("id", "testdiv");
|
||||
this.$element.html("test");
|
||||
}
|
||||
});
|
||||
var x = new Claz();
|
||||
x.appendTo($("body"));
|
||||
var $el = $("#testdiv");
|
||||
equal($el.length, 1);
|
||||
equal($el.parents()[0], $("body")[0]);
|
||||
equal($el.html(), "test");
|
||||
|
||||
var y = new Claz(x);
|
||||
equal(y.getParent(), x);
|
||||
|
||||
x.destroy();
|
||||
$el = $("#testdiv");
|
||||
equal($el.length, 0);
|
||||
});
|
||||
|
|
@ -0,0 +1,999 @@
|
|||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
|
||||
// Baseline setup
|
||||
// --------------
|
||||
|
||||
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the `_` variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets returned to break out of a loop iteration.
|
||||
var breaker = {};
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||
|
||||
// All **ECMAScript 5** native function implementations that we hope to use
|
||||
// are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys,
|
||||
nativeBind = FuncProto.bind;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for **Node.js**, with
|
||||
// backwards-compatibility for the old `require()` API. If we're in
|
||||
// the browser, add `_` as a global object via a string identifier,
|
||||
// for Closure Compiler "advanced" mode.
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = _;
|
||||
}
|
||||
exports._ = _;
|
||||
} else {
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.3.1';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (_.has(obj, key)) {
|
||||
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||
_.map = _.collect = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
results[results.length] = iterator.call(context, value, index, list);
|
||||
});
|
||||
if (obj.length === +obj.length) results.length = obj.length;
|
||||
return results;
|
||||
};
|
||||
|
||||
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
}
|
||||
});
|
||||
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as `foldr`.
|
||||
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||
}
|
||||
var reversed = _.toArray(obj).reverse();
|
||||
if (context && !initial) iterator = _.bind(iterator, context);
|
||||
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||
_.find = _.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
any(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||
// Aliased as `select`.
|
||||
_.filter = _.select = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||
// Aliased as `any`.
|
||||
var any = _.some = _.any = function(obj, iterator, context) {
|
||||
iterator || (iterator = _.identity);
|
||||
var result = false;
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
// Aliased as `contains`.
|
||||
_.include = _.contains = function(obj, target) {
|
||||
var found = false;
|
||||
if (obj == null) return found;
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
found = any(obj, function(value) {
|
||||
return value === target;
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method (with arguments) on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of `map`: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum element or (element-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return Infinity;
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Shuffle an array.
|
||||
_.shuffle = function(obj) {
|
||||
var shuffled = [], rand;
|
||||
each(obj, function(value, index, list) {
|
||||
if (index == 0) {
|
||||
shuffled[0] = value;
|
||||
} else {
|
||||
rand = Math.floor(Math.random() * (index + 1));
|
||||
shuffled[index] = shuffled[rand];
|
||||
shuffled[rand] = value;
|
||||
}
|
||||
});
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion. Pass either a string attribute
|
||||
// to group by, or a function that returns the criterion.
|
||||
_.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Safely convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// Array Functions
|
||||
// ---------------
|
||||
|
||||
// Get the first element of an array. Passing **n** will return the first N
|
||||
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||
// with `_.map`.
|
||||
_.first = _.head = function(array, n, guard) {
|
||||
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the last entry of the array. Especcialy useful on
|
||||
// the arguments object. Passing **n** will return all the values in
|
||||
// the array, excluding the last N. The **guard** check allows it to work with
|
||||
// `_.map`.
|
||||
_.initial = function(array, n, guard) {
|
||||
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
||||
};
|
||||
|
||||
// Get the last element of an array. Passing **n** will return the last N
|
||||
// values in the array. The **guard** check allows it to work with `_.map`.
|
||||
_.last = function(array, n, guard) {
|
||||
if ((n != null) && !guard) {
|
||||
return slice.call(array, Math.max(array.length - n, 0));
|
||||
} else {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||
// Especially useful on the arguments object. Passing an **index** will return
|
||||
// the rest of the values in the array from that index onward. The **guard**
|
||||
// check allows it to work with `_.map`.
|
||||
_.rest = _.tail = function(array, index, guard) {
|
||||
return slice.call(array, (index == null) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array, shallow) {
|
||||
return _.reduce(array, function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
||||
memo[memo.length] = value;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
// Aliased as `unique`.
|
||||
_.uniq = _.unique = function(array, isSorted, iterator) {
|
||||
var initial = iterator ? _.map(array, iterator) : array;
|
||||
var result = [];
|
||||
_.reduce(initial, function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
|
||||
memo[memo.length] = el;
|
||||
result[result.length] = array[i];
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments, true));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and a number of other arrays.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array) {
|
||||
var rest = _.flatten(slice.call(arguments, 1));
|
||||
return _.filter(array, function(value){ return !_.include(rest, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = slice.call(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||
// we need this function. Return the position of the first occurrence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||
// If the array is large and already in sort order, pass `true`
|
||||
// for **isSorted** to use binary search.
|
||||
_.indexOf = function(array, item, isSorted) {
|
||||
if (array == null) return -1;
|
||||
var i, l;
|
||||
if (isSorted) {
|
||||
i = _.sortedIndex(array, item);
|
||||
return array[i] === item ? i : -1;
|
||||
}
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (array == null) return -1;
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python `range()` function. See
|
||||
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||
_.range = function(start, stop, step) {
|
||||
if (arguments.length <= 1) {
|
||||
stop = start || 0;
|
||||
start = 0;
|
||||
}
|
||||
step = arguments[2] || 1;
|
||||
|
||||
var len = Math.max(Math.ceil((stop - start) / step), 0);
|
||||
var idx = 0;
|
||||
var range = new Array(len);
|
||||
|
||||
while(idx < len) {
|
||||
range[idx++] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
// Function (ahem) Functions
|
||||
// ------------------
|
||||
|
||||
// Reusable constructor function for prototype setting.
|
||||
var ctor = function(){};
|
||||
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function bind(func, context) {
|
||||
var bound, args;
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
if (!_.isFunction(func)) throw new TypeError;
|
||||
args = slice.call(arguments, 2);
|
||||
return bound = function() {
|
||||
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
||||
ctor.prototype = func.prototype;
|
||||
var self = new ctor;
|
||||
var result = func.apply(self, args.concat(slice.call(arguments)));
|
||||
if (Object(result) === result) return result;
|
||||
return self;
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = slice.call(arguments, 1);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||
};
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
_.throttle = function(func, wait) {
|
||||
var context, args, timeout, throttling, more;
|
||||
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
||||
return function() {
|
||||
context = this; args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (more) func.apply(context, args);
|
||||
whenDone();
|
||||
};
|
||||
if (!timeout) timeout = setTimeout(later, wait);
|
||||
if (throttling) {
|
||||
more = true;
|
||||
} else {
|
||||
func.apply(context, args);
|
||||
}
|
||||
whenDone();
|
||||
throttling = true;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
_.debounce = function(func, wait) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will be executed at most one time, no matter how
|
||||
// often you call it. Useful for lazy initialization.
|
||||
_.once = function(func) {
|
||||
var ran = false, memo;
|
||||
return function() {
|
||||
if (ran) return memo;
|
||||
ran = true;
|
||||
return memo = func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(slice.call(arguments, 0));
|
||||
return wrapper.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = arguments;
|
||||
return function() {
|
||||
var args = arguments;
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
if (times <= 0) return func();
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
||||
var keys = [];
|
||||
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
if (!_.isObject(obj)) return obj;
|
||||
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in
|
||||
// order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Internal recursive comparison function.
|
||||
function eq(a, b, stack) {
|
||||
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
||||
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
||||
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
||||
// A strict comparison is necessary because `null == undefined`.
|
||||
if (a == null || b == null) return a === b;
|
||||
// Unwrap any wrapped objects.
|
||||
if (a._chain) a = a._wrapped;
|
||||
if (b._chain) b = b._wrapped;
|
||||
// Invoke a custom `isEqual` method if one is provided.
|
||||
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
||||
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
||||
// Compare `[[Class]]` names.
|
||||
var className = toString.call(a);
|
||||
if (className != toString.call(b)) return false;
|
||||
switch (className) {
|
||||
// Strings, numbers, dates, and booleans are compared by value.
|
||||
case '[object String]':
|
||||
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
||||
// equivalent to `new String("5")`.
|
||||
return a == String(b);
|
||||
case '[object Number]':
|
||||
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
||||
// other numeric values.
|
||||
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
||||
case '[object Date]':
|
||||
case '[object Boolean]':
|
||||
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
||||
// millisecond representations. Note that invalid dates with millisecond representations
|
||||
// of `NaN` are not equivalent.
|
||||
return +a == +b;
|
||||
// RegExps are compared by their source patterns and flags.
|
||||
case '[object RegExp]':
|
||||
return a.source == b.source &&
|
||||
a.global == b.global &&
|
||||
a.multiline == b.multiline &&
|
||||
a.ignoreCase == b.ignoreCase;
|
||||
}
|
||||
if (typeof a != 'object' || typeof b != 'object') return false;
|
||||
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
||||
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
||||
var length = stack.length;
|
||||
while (length--) {
|
||||
// Linear search. Performance is inversely proportional to the number of
|
||||
// unique nested structures.
|
||||
if (stack[length] == a) return true;
|
||||
}
|
||||
// Add the first object to the stack of traversed objects.
|
||||
stack.push(a);
|
||||
var size = 0, result = true;
|
||||
// Recursively compare objects and arrays.
|
||||
if (className == '[object Array]') {
|
||||
// Compare array lengths to determine if a deep comparison is necessary.
|
||||
size = a.length;
|
||||
result = size == b.length;
|
||||
if (result) {
|
||||
// Deep compare the contents, ignoring non-numeric properties.
|
||||
while (size--) {
|
||||
// Ensure commutative equality for sparse arrays.
|
||||
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Objects with different constructors are not equivalent.
|
||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
||||
// Deep compare objects.
|
||||
for (var key in a) {
|
||||
if (_.has(a, key)) {
|
||||
// Count the expected number of properties.
|
||||
size++;
|
||||
// Deep compare each member.
|
||||
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
||||
}
|
||||
}
|
||||
// Ensure that both objects contain the same number of properties.
|
||||
if (result) {
|
||||
for (key in b) {
|
||||
if (_.has(b, key) && !(size--)) break;
|
||||
}
|
||||
result = !size;
|
||||
}
|
||||
}
|
||||
// Remove the first object from the stack of traversed objects.
|
||||
stack.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
return eq(a, b, []);
|
||||
};
|
||||
|
||||
// Is a given array, string, or object empty?
|
||||
// An "empty" object has no enumerable own-properties.
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (_.has(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return toString.call(obj) == '[object Arguments]';
|
||||
};
|
||||
if (!_.isArguments(arguments)) {
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && _.has(obj, 'callee'));
|
||||
};
|
||||
}
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return toString.call(obj) == '[object Function]';
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return toString.call(obj) == '[object String]';
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return toString.call(obj) == '[object Number]';
|
||||
};
|
||||
|
||||
// Is the given value `NaN`?
|
||||
_.isNaN = function(obj) {
|
||||
// `NaN` is the only value for which `===` is not reflexive.
|
||||
return obj !== obj;
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return toString.call(obj) == '[object Date]';
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return toString.call(obj) == '[object RegExp]';
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return obj === void 0;
|
||||
};
|
||||
|
||||
// Has own property?
|
||||
_.has = function(obj, key) {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
// -----------------
|
||||
|
||||
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function **n** times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Escape a string for HTML interpolation.
|
||||
_.escape = function(string) {
|
||||
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
evaluate : /<%([\s\S]+?)%>/g,
|
||||
interpolate : /<%=([\s\S]+?)%>/g,
|
||||
escape : /<%-([\s\S]+?)%>/g
|
||||
};
|
||||
|
||||
// When customizing `templateSettings`, if you don't want to define an
|
||||
// interpolation, evaluation or escaping regex, we need one that is
|
||||
// guaranteed not to match.
|
||||
var noMatch = /.^/;
|
||||
|
||||
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
||||
// that had been previously added.
|
||||
var unescape = function(code) {
|
||||
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
|
||||
};
|
||||
|
||||
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||
// and correctly escapes quotes within interpolated code.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
'with(obj||{}){__p.push(\'' +
|
||||
str.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(c.escape || noMatch, function(match, code) {
|
||||
return "',_.escape(" + unescape(code) + "),'";
|
||||
})
|
||||
.replace(c.interpolate || noMatch, function(match, code) {
|
||||
return "'," + unescape(code) + ",'";
|
||||
})
|
||||
.replace(c.evaluate || noMatch, function(match, code) {
|
||||
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
|
||||
})
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
+ "');}return __p.join('');";
|
||||
var func = new Function('obj', '_', tmpl);
|
||||
if (data) return func(data, _);
|
||||
return function(data) {
|
||||
return func.call(this, data, _);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a "chain" function, which will delegate to the wrapper.
|
||||
_.chain = function(obj) {
|
||||
return _(obj).chain();
|
||||
};
|
||||
|
||||
// The OOP Wrapper
|
||||
// ---------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Expose `wrapper.prototype` as `_.prototype`
|
||||
_.prototype = wrapper.prototype;
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = slice.call(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
var wrapped = this._wrapped;
|
||||
method.apply(wrapped, arguments);
|
||||
var length = wrapped.length;
|
||||
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
||||
return result(wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
}).call(this);
|
|
@ -0,0 +1,5 @@
|
|||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: 5adb2d9c89e53a6445e3799f9c4dc9110458c149
|
||||
branch: default
|
||||
latesttag: 0.5
|
||||
latesttagdistance: 9
|
|
@ -0,0 +1,13 @@
|
|||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2012
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
@ -0,0 +1,197 @@
|
|||
What
|
||||
====
|
||||
|
||||
``py.js`` is a parser and evaluator of Python expressions, written in
|
||||
pure javascript.
|
||||
|
||||
``py.js`` is not intended to implement a full Python interpreter
|
||||
(although it could be used for such an effort later on), its
|
||||
specification document is the `Python 2.7 Expressions spec
|
||||
<http://docs.python.org/reference/expressions.html>`_ (along with the
|
||||
lexical analysis part).
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
* Lambdas and ternaries should be parsed but are not implemented (in
|
||||
the evaluator)
|
||||
* Only floats are implemented, ``int`` literals are parsed as floats.
|
||||
* Octal and hexadecimal literals are not implemented
|
||||
* Srings are backed by JavaScript strings and probably behave like
|
||||
``unicode`` more than like ``str``
|
||||
* Slices don't work
|
||||
|
||||
Builtins
|
||||
--------
|
||||
|
||||
``py.js`` currently implements the following builtins:
|
||||
|
||||
``type``
|
||||
Restricted to creating new types, can't be used to get an object's
|
||||
type (yet)
|
||||
|
||||
``None``
|
||||
|
||||
``True``
|
||||
|
||||
``False``
|
||||
|
||||
``NotImplemented``
|
||||
Returned from rich comparison methods when the comparison is not
|
||||
implemented for this combination of operands. In ``py.js``, this
|
||||
is also the default implementation for all rich comparison methods.
|
||||
|
||||
``issubclass``
|
||||
|
||||
``object``
|
||||
|
||||
``bool``
|
||||
Does not inherit from ``int``, since ``int`` is not currently
|
||||
implemented.
|
||||
|
||||
``float``
|
||||
|
||||
``str``
|
||||
|
||||
``tuple``
|
||||
Constructor/coercer is not implemented, only handles literals
|
||||
|
||||
``list``
|
||||
Same as tuple (``list`` is currently an alias for ``tuple``)
|
||||
|
||||
``dict``
|
||||
Implements just about nothing
|
||||
|
||||
Note that most methods are probably missing from all of these.
|
||||
|
||||
Data model protocols
|
||||
--------------------
|
||||
|
||||
``py.js`` currently implements the following protocols (or
|
||||
sub-protocols) of the `Python 2.7 data model
|
||||
<http://docs.python.org/reference/datamodel.html>`_:
|
||||
|
||||
Rich comparisons
|
||||
Roughly complete implementation but for two limits: ``__eq__`` and
|
||||
``__ne__`` can't return ``NotImplemented`` (well they can but it's
|
||||
not going to work right), and the behavior is undefined if a
|
||||
rich-comparison operation does not return a ``py.bool``.
|
||||
|
||||
Also, a ``NotImplemented`` result does not try the reverse
|
||||
operation, not sure if it's supposed to. It directly falls back to
|
||||
comparing type names.
|
||||
|
||||
Boolean conversion
|
||||
Implementing ``__nonzero__`` should work.
|
||||
|
||||
Customizing attribute access
|
||||
Protocols for getting and setting attributes (including new-style
|
||||
extension) fully implemented but for ``__delattr__`` (since
|
||||
``del`` is a statement)
|
||||
|
||||
Descriptor protocol
|
||||
As with attributes, ``__delete__`` is not implemented.
|
||||
|
||||
Callable objects
|
||||
|
||||
Collections Abstract Base Classes
|
||||
Container is the only implemented ABC protocol (ABCs themselves
|
||||
are not currently implemented) (well technically Callable and
|
||||
Hashable are kind-of implemented as well)
|
||||
|
||||
Numeric type emulation
|
||||
Operators are implemented (but not tested), ``abs``, ``divmod``
|
||||
and ``pow`` builtins are not implemented yet. Neither are ``oct``
|
||||
and ``hex`` but I'm not sure we care (I'm not sure we care about
|
||||
``pow`` or even ``divmod`` either, for that matter)
|
||||
|
||||
Utilities
|
||||
---------
|
||||
|
||||
``py.js`` also provides (and exposes) a few utilities for "userland"
|
||||
implementation:
|
||||
|
||||
``def``
|
||||
Wraps a native javascript function into a ``py.js`` function, so
|
||||
that it can be called from native expressions.
|
||||
|
||||
Does not ensure the return types are type-compatible with
|
||||
``py.js`` types.
|
||||
|
||||
When accessing instance methods, ``py.js`` automatically wraps
|
||||
these in a variant of ``py.def`` automatically, to behave as
|
||||
Python's (bound) methods.
|
||||
|
||||
Why
|
||||
===
|
||||
|
||||
Originally, to learn about Pratt parsers (which are very, very good at
|
||||
parsing expressions with lots of infix or mixfix symbols). The
|
||||
evaluator part came because "why not" and because I work on a product
|
||||
with the "feature" of transmitting Python expressions (over the wire)
|
||||
which the client is supposed to evaluate.
|
||||
|
||||
How
|
||||
===
|
||||
|
||||
At this point, only three steps exist in ``py.js``: tokenizing,
|
||||
parsing and evaluation. It is possible that a compilation step be
|
||||
added later (for performance reasons).
|
||||
|
||||
To evaluate a Python expression, the caller merely needs to call
|
||||
`py.eval`_. `py.eval`_ takes a mandatory Python
|
||||
expression to evaluate (as a string) and an optional context, for the
|
||||
substitution of the free variables in the expression::
|
||||
|
||||
> py.eval("type in ('a', 'b', 'c') and foo", {type: 'c', foo: true});
|
||||
true
|
||||
|
||||
This is great for one-shot evaluation of expressions. If the
|
||||
expression will need to be repeatedly evaluated with the same
|
||||
parameters, the various parsing and evaluation steps can be performed
|
||||
separately: `py.eval`_ is really a shortcut for sequentially calling
|
||||
`py.tokenize`_, `py.parse`_ and `py.evaluate`_.
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. _py.eval:
|
||||
|
||||
``py.eval(expr[, context])``
|
||||
"Do everything" function, to use for one-shot evaluation of a
|
||||
Python expression: it will internally handle the tokenizing,
|
||||
parsing and actual evaluation of the Python expression without
|
||||
having to perform these separately.
|
||||
|
||||
``expr``
|
||||
Python expression to evaluate
|
||||
``context``
|
||||
context dictionary holding the substitutions for the free
|
||||
variables in the expression
|
||||
|
||||
.. _py.tokenize:
|
||||
|
||||
``py.tokenize(expr)``
|
||||
``expr``
|
||||
Python expression to tokenize
|
||||
|
||||
.. _py.parse:
|
||||
|
||||
``py.parse(tokens)``
|
||||
Parses a token stream and returns an abstract syntax tree of the
|
||||
expression (if the token stream represents a valid Python
|
||||
expression).
|
||||
|
||||
A parse tree is stateless and can be memoized and used multiple
|
||||
times in separate evaluations.
|
||||
|
||||
``tokens``
|
||||
stream of tokens returned by `py.tokenize`_
|
||||
|
||||
.. _py.evaluate:
|
||||
|
||||
``py.evaluate(ast[, context])``
|
||||
``ast``
|
||||
The output of `py.parse`_
|
||||
``context``
|
||||
The evaluation context for the Python expression.
|
|
@ -0,0 +1,47 @@
|
|||
* 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
|
||||
---------
|
||||
|
||||
* Builtins should be built-in, there should be no need to add e.g. ``py.bool`` to the evaluation context (?)
|
||||
* 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)
|
||||
|
||||
Base type requirements:
|
||||
***********************
|
||||
|
||||
* int
|
||||
* float
|
||||
* --str-- unicode
|
||||
* bool
|
||||
* dict
|
||||
* tuple
|
||||
* list
|
||||
* ?module
|
||||
* ?object
|
||||
* datetime.time
|
||||
* datetime.timedelta
|
||||
* NotImplementedType
|
||||
|
||||
Base methods requirement
|
||||
************************
|
||||
|
||||
* ``__getattr__``
|
||||
* ``dict.get``
|
||||
* ``__len__``
|
||||
|
||||
In datamodel, not implemented in any type, untested
|
||||
***************************************************
|
||||
|
||||
* a[b]
|
||||
|
||||
* a + b, a - b, a * b, ...
|
||||
|
||||
* +a, ~a
|
|
@ -0,0 +1,905 @@
|
|||
var py = {};
|
||||
(function (py) {
|
||||
var create = function (o, props) {
|
||||
function F() {}
|
||||
F.prototype = o;
|
||||
var inst = new F;
|
||||
if (props) {
|
||||
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) {
|
||||
var s = symbol(id);
|
||||
s.id = '(constant)';
|
||||
s.value = id;
|
||||
s.nud = function () {
|
||||
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');
|
||||
|
||||
infix('=', 10, function (left) {
|
||||
if (left.id !== '(name)') {
|
||||
throw new Error("Expected keyword argument name, got " + token.id);
|
||||
}
|
||||
this.first = left;
|
||||
this.second = expression();
|
||||
return this;
|
||||
});
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
py.tokenize = (function () {
|
||||
function group() { return '(' + Array.prototype.join.call(arguments, '|') + ')'; }
|
||||
|
||||
var Whitespace = '[ \\f\\t]*';
|
||||
|
||||
var Name = '[a-zA-Z_]\\w*';
|
||||
|
||||
var DecNumber = '\\d+';
|
||||
var IntNumber = DecNumber;
|
||||
var PointFloat = group('\\d+\\.\\d*', '\\.\\d+');
|
||||
var FloatNumber = PointFloat;
|
||||
var Number = group(FloatNumber, IntNumber);
|
||||
|
||||
var Operator = group("\\*\\*=?", ">>=?", "<<=?", "<>", "!=",
|
||||
"//=?", "[+\\-*/%&|^=<>]=?", "~");
|
||||
var Bracket = '[\\[\\]\\(\\)\\{\\}]';
|
||||
var Special = '[:;.,`@]';
|
||||
var Funny = group(Operator, Bracket, Special);
|
||||
|
||||
var ContStr = group("'[^']*'", '"[^"]*"');
|
||||
|
||||
var PseudoToken = Whitespace + group(Number, Funny, ContStr, Name);
|
||||
|
||||
return function tokenize(s) {
|
||||
var max=s.length, tokens = [];
|
||||
// /g flag makes repeated exec() have memory
|
||||
var pseudoprog = new RegExp(PseudoToken, 'g');
|
||||
|
||||
while(pseudoprog.lastIndex < max) {
|
||||
var pseudomatch = pseudoprog.exec(s);
|
||||
if (!pseudomatch) {
|
||||
// if match failed on trailing whitespace, end tokenizing
|
||||
if (/^\s+$/.test(s.slice(end))) {
|
||||
break;
|
||||
}
|
||||
throw new Error('Failed to tokenize <<' + s
|
||||
+ '>> at index ' + (end || 0)
|
||||
+ '; parsed so far: ' + tokens);
|
||||
}
|
||||
|
||||
var start = pseudomatch.index, end = pseudoprog.lastIndex;
|
||||
// strip leading space caught by Whitespace
|
||||
var token = s.slice(start, end).replace(new RegExp('^' + Whitespace), '');
|
||||
var initial = token[0];
|
||||
|
||||
if (/\d/.test(initial) || (initial === '.' && token !== '.')) {
|
||||
tokens.push(create(symbols['(number)'], {
|
||||
value: parseFloat(token)
|
||||
}));
|
||||
} else if (/'|"/.test(initial)) {
|
||||
tokens.push(create(symbols['(string)'], {
|
||||
value: token.slice(1, -1)
|
||||
}));
|
||||
} else if (token in symbols) {
|
||||
var symbol;
|
||||
// transform 'not in' and 'is not' in a single token
|
||||
if (token === 'in' && tokens[tokens.length-1].id === 'not') {
|
||||
symbol = symbols['not in'];
|
||||
tokens.pop();
|
||||
} else if (token === 'not' && tokens[tokens.length-1].id === 'is') {
|
||||
symbol = symbols['is not'];
|
||||
tokens.pop();
|
||||
} else {
|
||||
symbol = symbols[token];
|
||||
}
|
||||
tokens.push(create(symbol));
|
||||
} else if (/[_a-zA-Z]/.test(initial)) {
|
||||
tokens.push(create(symbols['(name)'], {
|
||||
value: token
|
||||
}));
|
||||
} else {
|
||||
throw new Error("Tokenizing failure of <<" + s + ">> at index " + start
|
||||
+ " for token [[" + token + "]]"
|
||||
+ "; parsed so far: " + tokens);
|
||||
|
||||
}
|
||||
}
|
||||
tokens.push(create(symbols['(end)']));
|
||||
return 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();
|
||||
}
|
||||
|
||||
function PY_ensurepy(val, name) {
|
||||
switch (val) {
|
||||
case undefined:
|
||||
throw new Error("NameError: name '" + name + "' is not defined");
|
||||
case null:
|
||||
return py.None;
|
||||
case true:
|
||||
return py.True;
|
||||
case false:
|
||||
return py.False;
|
||||
}
|
||||
|
||||
if (val instanceof py.object
|
||||
|| val === py.object
|
||||
|| py.issubclass.__call__([val, py.object]) === py.True) {
|
||||
return val;
|
||||
}
|
||||
|
||||
switch (typeof val) {
|
||||
case 'number':
|
||||
return new py.float(val);
|
||||
case 'string':
|
||||
return new py.str(val);
|
||||
case 'function':
|
||||
return new py.def(val);
|
||||
}
|
||||
|
||||
throw new Error("Could not convert " + val + " to a pyval");
|
||||
}
|
||||
// Builtins
|
||||
py.type = function type(constructor, base, dict) {
|
||||
var proto;
|
||||
if (!base) {
|
||||
base = py.object;
|
||||
}
|
||||
proto = constructor.prototype = create(base.prototype);
|
||||
proto.constructor = constructor;
|
||||
if (dict) {
|
||||
for(var k in dict) {
|
||||
if (!dict.hasOwnProperty(k)) { continue; }
|
||||
proto[k] = dict[k];
|
||||
}
|
||||
}
|
||||
constructor.__call__ = function () {
|
||||
// create equivalent type with same prototype
|
||||
var instance = create(proto);
|
||||
// call actual constructor
|
||||
var res = constructor.apply(instance, arguments);
|
||||
// return result of constructor if any, otherwise instance
|
||||
return res || instance;
|
||||
};
|
||||
return constructor;
|
||||
};
|
||||
|
||||
var hash_counter = 0;
|
||||
py.object = py.type(function object() {}, {}, {
|
||||
// Basic customization
|
||||
__hash__: function () {
|
||||
if (this._hash) {
|
||||
return this._hash;
|
||||
}
|
||||
return this._hash = hash_counter++;
|
||||
},
|
||||
__eq__: function (other) {
|
||||
return (this === other) ? py.True : py.False;
|
||||
},
|
||||
__ne__: function (other) {
|
||||
if (this.__eq__(other) === py.True) {
|
||||
return py.False;
|
||||
} else {
|
||||
return py.True;
|
||||
}
|
||||
},
|
||||
__lt__: function () { return py.NotImplemented; },
|
||||
__le__: function () { return py.NotImplemented; },
|
||||
__ge__: function () { return py.NotImplemented; },
|
||||
__gt__: function () { return py.NotImplemented; },
|
||||
__str__: function () {
|
||||
return this.__unicode__();
|
||||
},
|
||||
__unicode__: function () {
|
||||
// TODO: return python string
|
||||
return '<object ' + this.constructor.name + '>';
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return py.True;
|
||||
},
|
||||
// Attribute access
|
||||
__getattribute__: function (name) {
|
||||
if (name in this) {
|
||||
var val = this[name];
|
||||
if ('__get__' in val) {
|
||||
// TODO: second argument should be class
|
||||
return val.__get__(this);
|
||||
}
|
||||
if (typeof val === 'function' && !this.hasOwnProperty(name)) {
|
||||
// val is a method from the class
|
||||
return new PY_instancemethod(val, this);
|
||||
}
|
||||
return PY_ensurepy(val);
|
||||
}
|
||||
if ('__getattr__' in this) {
|
||||
return this.__getattr__(name);
|
||||
}
|
||||
throw new Error("AttributeError: object has no attribute '" + name +"'");
|
||||
},
|
||||
__setattr__: function (name, value) {
|
||||
if (name in this && '__set__' in this[name]) {
|
||||
this[name].__set__(this, value);
|
||||
}
|
||||
this[name] = value;
|
||||
},
|
||||
// no delattr, because no 'del' statement
|
||||
|
||||
// Conversion
|
||||
toJSON: function () {
|
||||
throw new Error(this.constructor.name + ' can not be converted to JSON');
|
||||
}
|
||||
});
|
||||
var NoneType = py.type(function NoneType() {}, py.object, {
|
||||
__nonzero__: function () { return py.False; },
|
||||
toJSON: function () { return null; }
|
||||
});
|
||||
py.None = new NoneType();
|
||||
var NotImplementedType = py.type(function NotImplementedType(){});
|
||||
py.NotImplemented = new NotImplementedType();
|
||||
var booleans_initialized = false;
|
||||
py.bool = py.type(function bool(value) {
|
||||
value = (value instanceof Array) ? value[0] : value;
|
||||
// The only actual instance of py.bool should be py.True
|
||||
// and py.False. Return the new instance of py.bool if we
|
||||
// are initializing py.True and py.False, otherwise always
|
||||
// return either py.True or py.False.
|
||||
if (!booleans_initialized) {
|
||||
return;
|
||||
}
|
||||
if (value === undefined) { return py.False; }
|
||||
return value.__nonzero__() === py.True ? py.True : py.False;
|
||||
}, py.object, {
|
||||
__nonzero__: function () { return this; },
|
||||
toJSON: function () { return this === py.True; }
|
||||
});
|
||||
py.True = new py.bool();
|
||||
py.False = new py.bool();
|
||||
booleans_initialized = true;
|
||||
py.float = py.type(function float(value) {
|
||||
value = (value instanceof Array) ? value[0] : value;
|
||||
if (value === undefined) { this._value = 0; return; }
|
||||
if (value instanceof py.float) { return value; }
|
||||
if (typeof value === 'number' || value instanceof Number) {
|
||||
this._value = value;
|
||||
return;
|
||||
}
|
||||
if (typeof value === 'string' || value instanceof String) {
|
||||
this._value = parseFloat(value);
|
||||
return;
|
||||
}
|
||||
if (value instanceof py.object && '__float__' in value) {
|
||||
var res = value.__float__();
|
||||
if (res instanceof py.float) {
|
||||
return res;
|
||||
}
|
||||
throw new Error('TypeError: __float__ returned non-float (type ' +
|
||||
res.constructor.name + ')');
|
||||
}
|
||||
throw new Error('TypeError: float() argument must be a string or a number');
|
||||
}, py.object, {
|
||||
__eq__: function (other) {
|
||||
return this._value === other._value ? py.True : py.False;
|
||||
},
|
||||
__lt__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return this._value < other._value ? py.True : py.False;
|
||||
},
|
||||
__le__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return this._value <= other._value ? py.True : py.False;
|
||||
},
|
||||
__gt__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return this._value > other._value ? py.True : py.False;
|
||||
},
|
||||
__ge__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return this._value >= other._value ? py.True : py.False;
|
||||
},
|
||||
__add__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return new py.float(this._value + other._value);
|
||||
},
|
||||
__neg__: function () {
|
||||
return new py.float(-this._value);
|
||||
},
|
||||
__sub__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return new py.float(this._value - other._value);
|
||||
},
|
||||
__mul__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return new py.float(this._value * other._value);
|
||||
},
|
||||
__div__: function (other) {
|
||||
if (!(other instanceof py.float)) { return py.NotImplemented; }
|
||||
return new py.float(this._value / other._value);
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return this._value ? py.True : py.False;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._value;
|
||||
}
|
||||
});
|
||||
py.str = py.type(function str(s) {
|
||||
s = (s instanceof Array) ? s[0] : s;
|
||||
if (s === undefined) { this._value = ''; return; }
|
||||
if (s instanceof py.str) { return s; }
|
||||
if (typeof s === 'string' || s instanceof String) {
|
||||
this._value = s;
|
||||
return;
|
||||
}
|
||||
var v = s.__str__();
|
||||
if (v instanceof py.str) { return v; }
|
||||
throw new Error('TypeError: __str__ returned non-string (type ' +
|
||||
v.constructor.name + ')');
|
||||
}, py.object, {
|
||||
__eq__: function (other) {
|
||||
if (other instanceof py.str && this._value === other._value) {
|
||||
return py.True;
|
||||
}
|
||||
return py.False;
|
||||
},
|
||||
__lt__: function (other) {
|
||||
if (!(other instanceof py.str)) { return py.NotImplemented; }
|
||||
return this._value < other._value ? py.True : py.False;
|
||||
},
|
||||
__le__: function (other) {
|
||||
if (!(other instanceof py.str)) { return py.NotImplemented; }
|
||||
return this._value <= other._value ? py.True : py.False;
|
||||
},
|
||||
__gt__: function (other) {
|
||||
if (!(other instanceof py.str)) { return py.NotImplemented; }
|
||||
return this._value > other._value ? py.True : py.False;
|
||||
},
|
||||
__ge__: function (other) {
|
||||
if (!(other instanceof py.str)) { return py.NotImplemented; }
|
||||
return this._value >= other._value ? py.True : py.False;
|
||||
},
|
||||
__add__: function (other) {
|
||||
if (!(other instanceof py.str)) { return py.NotImplemented; }
|
||||
return new py.str(this._value + other._value);
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return this._value.length ? py.True : py.False;
|
||||
},
|
||||
__contains__: function (s) {
|
||||
return (this._value.indexOf(s._value) !== -1) ? py.True : py.False;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._value;
|
||||
}
|
||||
});
|
||||
py.tuple = py.type(function tuple() {}, null, {
|
||||
__contains__: function (value) {
|
||||
for(var i=0, len=this.values.length; i<len; ++i) {
|
||||
if (this.values[i].__eq__(value) === py.True) {
|
||||
return py.True;
|
||||
}
|
||||
}
|
||||
return py.False;
|
||||
},
|
||||
toJSON: function () {
|
||||
var out = [];
|
||||
for (var i=0; i<this.values.length; ++i) {
|
||||
out.push(this.values[i].toJSON());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
py.list = py.tuple;
|
||||
py.dict = py.type(function dict() {
|
||||
this._store = {};
|
||||
}, py.object, {
|
||||
__setitem__: function (key, value) {
|
||||
this._store[key.__hash__()] = [key, value];
|
||||
},
|
||||
toJSON: function () {
|
||||
var out = {};
|
||||
for(var k in this._store) {
|
||||
var item = this._store[k];
|
||||
out[item[0].toJSON()] = item[1].toJSON();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
py.def = py.type(function def(nativefunc) {
|
||||
this._inst = null;
|
||||
this._func = nativefunc;
|
||||
}, py.object, {
|
||||
__call__: function (args, kwargs) {
|
||||
// don't want to rewrite __call__ for instancemethod
|
||||
return this._func.call(this._inst, args, kwargs);
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._func;
|
||||
}
|
||||
});
|
||||
var PY_instancemethod = py.type(function instancemethod(nativefunc, instance, _cls) {
|
||||
// could also use bind?
|
||||
this._inst = instance;
|
||||
this._func = nativefunc;
|
||||
}, py.def, {});
|
||||
|
||||
py.issubclass = new py.def(function issubclass(args) {
|
||||
var derived = args[0], parent = args[1];
|
||||
// still hurts my brain that this can work
|
||||
return derived.prototype instanceof py.object
|
||||
? py.True
|
||||
: py.False;
|
||||
});
|
||||
|
||||
|
||||
// All binary operators with fallbacks, so they can be applied generically
|
||||
var PY_operators = {
|
||||
'==': ['eq', 'eq', function (a, b) { return a === b; }],
|
||||
'!=': ['ne', 'ne', function (a, b) { return a !== b; }],
|
||||
'<': ['lt', 'gt', function (a, b) {return a.constructor.name < b.constructor.name;}],
|
||||
'<=': ['le', 'ge', function (a, b) {return a.constructor.name <= b.constructor.name;}],
|
||||
'>': ['gt', 'lt', function (a, b) {return a.constructor.name > b.constructor.name;}],
|
||||
'>=': ['ge', 'le', function (a, b) {return a.constructor.name >= b.constructor.name;}],
|
||||
|
||||
'+': ['add', 'radd'],
|
||||
'-': ['sub', 'rsub'],
|
||||
'*': ['mul', 'rmul'],
|
||||
'/': ['div', 'rdiv'],
|
||||
'//': ['floordiv', 'rfloordiv'],
|
||||
'%': ['mod', 'rmod'],
|
||||
'**': ['pow', 'rpow'],
|
||||
'<<': ['lshift', 'rlshift'],
|
||||
'>>': ['rshift', 'rrshift'],
|
||||
'&': ['and', 'rand'],
|
||||
'^': ['xor', 'rxor'],
|
||||
'|': ['or', 'ror']
|
||||
};
|
||||
/**
|
||||
* Implements operator fallback/reflection.
|
||||
*
|
||||
* First two arguments are the objects to apply the operator on,
|
||||
* in their actual order (ltr).
|
||||
*
|
||||
* Third argument is the actual operator.
|
||||
*
|
||||
* If the operator methods raise exceptions, those exceptions are
|
||||
* not intercepted.
|
||||
*/
|
||||
var PY_op = function (o1, o2, op) {
|
||||
var r;
|
||||
var methods = PY_operators[op];
|
||||
var forward = '__' + methods[0] + '__', reverse = '__' + methods[1] + '__';
|
||||
var otherwise = methods[2];
|
||||
|
||||
if (forward in o1 && (r = o1[forward](o2)) !== py.NotImplemented) {
|
||||
return r;
|
||||
}
|
||||
if (reverse in o2 && (r = o2[reverse](o1)) !== py.NotImplemented) {
|
||||
return r;
|
||||
}
|
||||
if (otherwise) {
|
||||
return PY_ensurepy(otherwise(o1, o2));
|
||||
}
|
||||
throw new Error(
|
||||
"TypeError: unsupported operand type(s) for " + op + ": '"
|
||||
+ o1.constructor.name + "' and '"
|
||||
+ o2.constructor.name + "'");
|
||||
};
|
||||
|
||||
var PY_builtins = {
|
||||
type: py.type,
|
||||
|
||||
None: py.None,
|
||||
True: py.True,
|
||||
False: py.False,
|
||||
NotImplemented: py.NotImplemented,
|
||||
|
||||
object: py.object,
|
||||
bool: py.bool,
|
||||
float: py.float,
|
||||
tuple: py.tuple,
|
||||
list: py.list,
|
||||
dict: py.dict,
|
||||
issubclass: py.issubclass
|
||||
};
|
||||
|
||||
py.parse = function (toks) {
|
||||
var index = 0;
|
||||
token = toks[0];
|
||||
next = function () { return toks[++index]; };
|
||||
return expression();
|
||||
};
|
||||
var evaluate_operator = function (operator, a, b) {
|
||||
var v;
|
||||
switch (operator) {
|
||||
case 'is': return a === b ? py.True : py.False;
|
||||
case 'is not': return a !== b ? py.True : py.False;
|
||||
case 'in':
|
||||
return b.__contains__(a);
|
||||
case 'not in':
|
||||
return b.__contains__(a) === py.True ? py.False : py.True;
|
||||
case '==': case '!=':
|
||||
case '<': case '<=':
|
||||
case '>': case '>=':
|
||||
return PY_op(a, b, operator);
|
||||
}
|
||||
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
|
||||
};
|
||||
py.evaluate = function (expr, context) {
|
||||
context = context || {};
|
||||
switch (expr.id) {
|
||||
case '(name)':
|
||||
var val = context[expr.value];
|
||||
if (val === undefined && expr.value in PY_builtins) {
|
||||
return PY_builtins[expr.value];
|
||||
}
|
||||
return PY_ensurepy(val, expr.value);
|
||||
case '(string)':
|
||||
return new py.str(expr.value);
|
||||
case '(number)':
|
||||
return new py.float(expr.value);
|
||||
case '(constant)':
|
||||
switch (expr.value) {
|
||||
case 'None': return py.None;
|
||||
case 'False': return py.False;
|
||||
case 'True': return py.True;
|
||||
}
|
||||
throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
|
||||
case '(comparator)':
|
||||
var result, left = py.evaluate(expr.expressions[0], context);
|
||||
for(var i=0; i<expr.operators.length; ++i) {
|
||||
result = evaluate_operator(
|
||||
expr.operators[i],
|
||||
left,
|
||||
left = py.evaluate(expr.expressions[i+1], context));
|
||||
if (result === py.False) { return py.False; }
|
||||
}
|
||||
return py.True;
|
||||
case 'not':
|
||||
return py.evaluate(expr.first, context).__nonzero__() === py.True
|
||||
? py.False
|
||||
: py.True;
|
||||
case 'and':
|
||||
var and_first = py.evaluate(expr.first, context);
|
||||
if (and_first.__nonzero__() === py.True) {
|
||||
return py.evaluate(expr.second, context);
|
||||
}
|
||||
return and_first;
|
||||
case 'or':
|
||||
var or_first = py.evaluate(expr.first, context);
|
||||
if (or_first.__nonzero__() === py.True) {
|
||||
return or_first
|
||||
}
|
||||
return py.evaluate(expr.second, context);
|
||||
case '(':
|
||||
if (expr.second) {
|
||||
var callable = py.evaluate(expr.first, context);
|
||||
var args = [], kwargs = {};
|
||||
for (var jj=0; jj<expr.second.length; ++jj) {
|
||||
var arg = expr.second[jj];
|
||||
if (arg.id !== '=') {
|
||||
// arg
|
||||
args.push(py.evaluate(arg, context));
|
||||
} else {
|
||||
// kwarg
|
||||
kwargs[arg.first.value] =
|
||||
py.evaluate(arg.second, context);
|
||||
}
|
||||
}
|
||||
return callable.__call__(args, kwargs);
|
||||
}
|
||||
var tuple_exprs = expr.first,
|
||||
tuple_values = [];
|
||||
for (var j=0, len=tuple_exprs.length; j<len; ++j) {
|
||||
tuple_values.push(py.evaluate(
|
||||
tuple_exprs[j], context));
|
||||
}
|
||||
var t = new py.tuple();
|
||||
t.values = tuple_values;
|
||||
return t;
|
||||
case '[':
|
||||
if (expr.second) {
|
||||
return py.evaluate(expr.first, context)
|
||||
.__getitem__(expr.evaluate(expr.second, context));
|
||||
}
|
||||
var list_exprs = expr.first, list_values = [];
|
||||
for (var k=0; k<list_exprs.length; ++k) {
|
||||
list_values.push(py.evaluate(
|
||||
list_exprs[k], context));
|
||||
}
|
||||
var l = new py.list();
|
||||
l.values = list_values;
|
||||
return l;
|
||||
case '{':
|
||||
var dict_exprs = expr.first, dict = new py.dict;
|
||||
for(var l=0; l<dict_exprs.length; ++l) {
|
||||
dict.__setitem__(
|
||||
py.evaluate(dict_exprs[l][0], context),
|
||||
py.evaluate(dict_exprs[l][1], context));
|
||||
}
|
||||
return dict;
|
||||
case '.':
|
||||
if (expr.second.id !== '(name)') {
|
||||
throw new Error('SyntaxError: ' + expr);
|
||||
}
|
||||
return py.evaluate(expr.first, context)
|
||||
.__getattribute__(expr.second.value);
|
||||
// numerical operators
|
||||
case '~':
|
||||
return (py.evaluate(expr.first, context)).__invert__();
|
||||
case '+':
|
||||
if (!expr.second) {
|
||||
return (py.evaluate(expr.first, context)).__pos__();
|
||||
}
|
||||
case '-':
|
||||
if (!expr.second) {
|
||||
return (py.evaluate(expr.first, context)).__neg__();
|
||||
}
|
||||
case '*': case '/': case '//':
|
||||
case '%':
|
||||
case '**':
|
||||
case '<<': case '>>':
|
||||
case '&': case '^': case '|':
|
||||
return PY_op(
|
||||
py.evaluate(expr.first, context),
|
||||
py.evaluate(expr.second, context),
|
||||
expr.id);
|
||||
|
||||
default:
|
||||
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
|
||||
}
|
||||
};
|
||||
py.eval = function (str, context) {
|
||||
return py.evaluate(
|
||||
py.parse(
|
||||
py.tokenize(
|
||||
str)),
|
||||
context).toJSON();
|
||||
}
|
||||
})(typeof exports === 'undefined' ? py : exports);
|
|
@ -0,0 +1,132 @@
|
|||
var py = require('../lib/py.js'),
|
||||
expect = require('expect.js');
|
||||
|
||||
expect.Assertion.prototype.tokens = function (n) {
|
||||
var length = this.obj.length;
|
||||
this.assert(length === n + 1,
|
||||
'expected ' + this.obj + ' to have ' + n + ' tokens',
|
||||
'expected ' + this.obj + ' to not have ' + n + ' tokens');
|
||||
this.assert(this.obj[length-1].id === '(end)',
|
||||
'expected ' + this.obj + ' to have and end token',
|
||||
'expected ' + this.obj + ' to not have an end token');
|
||||
};
|
||||
|
||||
expect.Assertion.prototype.named = function (value) {
|
||||
this.assert(this.obj.id === '(name)',
|
||||
'expected ' + this.obj + ' to be a name token',
|
||||
'expected ' + this.obj + ' not to be a name token');
|
||||
this.assert(this.obj.value === value,
|
||||
'expected ' + this.obj + ' to have tokenized ' + value,
|
||||
'expected ' + this.obj + ' not to have tokenized ' + value);
|
||||
};
|
||||
expect.Assertion.prototype.constant = function (value) {
|
||||
this.assert(this.obj.id === '(constant)',
|
||||
'expected ' + this.obj + ' to be a constant token',
|
||||
'expected ' + this.obj + ' not to be a constant token');
|
||||
this.assert(this.obj.value === value,
|
||||
'expected ' + this.obj + ' to have tokenized ' + value,
|
||||
'expected ' + this.obj + ' not to have tokenized ' + value);
|
||||
};
|
||||
expect.Assertion.prototype.number = function (value) {
|
||||
this.assert(this.obj.id === '(number)',
|
||||
'expected ' + this.obj + ' to be a number token',
|
||||
'expected ' + this.obj + ' not to be a number token');
|
||||
this.assert(this.obj.value === value,
|
||||
'expected ' + this.obj + ' to have tokenized ' + value,
|
||||
'expected ' + this.obj + ' not to have tokenized ' + value);
|
||||
};
|
||||
expect.Assertion.prototype.string = function (value) {
|
||||
this.assert(this.obj.id === '(string)',
|
||||
'expected ' + this.obj + ' to be a string token',
|
||||
'expected ' + this.obj + ' not to be a string token');
|
||||
this.assert(this.obj.value === value,
|
||||
'expected ' + this.obj + ' to have tokenized ' + value,
|
||||
'expected ' + this.obj + ' not to have tokenized ' + value);
|
||||
};
|
||||
|
||||
describe('Tokenizer', function () {
|
||||
describe('simple literals', function () {
|
||||
it('tokenizes numbers', function () {
|
||||
var toks = py.tokenize('1');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.number(1);
|
||||
|
||||
var toks = py.tokenize('-1');
|
||||
expect(toks).to.have.tokens(2);
|
||||
expect(toks[0].id).to.be('-');
|
||||
expect(toks[1]).to.be.number(1);
|
||||
|
||||
var toks = py.tokenize('1.2');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.number(1.2);
|
||||
|
||||
var toks = py.tokenize('.42');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.number(0.42);
|
||||
});
|
||||
it('tokenizes strings', function () {
|
||||
var toks = py.tokenize('"foo"');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.string('foo');
|
||||
|
||||
var toks = py.tokenize("'foo'");
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.string('foo');
|
||||
});
|
||||
it('tokenizes bare names', function () {
|
||||
var toks = py.tokenize('foo');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0].id).to.be('(name)');
|
||||
expect(toks[0].value).to.be('foo');
|
||||
});
|
||||
it('tokenizes constants', function () {
|
||||
var toks = py.tokenize('None');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.constant('None');
|
||||
|
||||
var toks = py.tokenize('True');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.constant('True');
|
||||
|
||||
var toks = py.tokenize('False');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.constant('False');
|
||||
});
|
||||
it('does not fuck up on trailing spaces', function () {
|
||||
var toks = py.tokenize('None ');
|
||||
expect(toks).to.have.tokens(1);
|
||||
expect(toks[0]).to.be.constant('None');
|
||||
});
|
||||
});
|
||||
describe('collections', function () {
|
||||
it('tokenizes opening and closing symbols', function () {
|
||||
var toks = py.tokenize('()');
|
||||
expect(toks).to.have.tokens(2);
|
||||
expect(toks[0].id).to.be('(');
|
||||
expect(toks[1].id).to.be(')');
|
||||
});
|
||||
});
|
||||
describe('functions', function () {
|
||||
it('tokenizes kwargs', function () {
|
||||
var toks = py.tokenize('foo(bar=3, qux=4)');
|
||||
expect(toks).to.have.tokens(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Parser', function () {
|
||||
describe('functions', function () {
|
||||
var ast = py.parse(py.tokenize('foo(bar=3, qux=4)'));
|
||||
expect(ast.id).to.be('(');
|
||||
expect(ast.first).to.be.named('foo');
|
||||
|
||||
args = ast.second;
|
||||
expect(args[0].id).to.be('=');
|
||||
expect(args[0].first).to.be.named('bar');
|
||||
expect(args[0].second).to.be.number(3);
|
||||
|
||||
expect(args[1].id).to.be('=');
|
||||
expect(args[1].first).to.be.named('qux');
|
||||
expect(args[1].second).to.be.number(4);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,379 @@
|
|||
var py = require('../lib/py.js'),
|
||||
expect = require('expect.js');
|
||||
|
||||
var ev = function (str, context) {
|
||||
return py.evaluate(py.parse(py.tokenize(str)), context);
|
||||
};
|
||||
|
||||
describe('Literals', function () {
|
||||
describe('Number', function () {
|
||||
it('should have the right type', function () {
|
||||
expect(ev('1')).to.be.a(py.float);
|
||||
});
|
||||
it('should yield the corresponding JS value', function () {
|
||||
expect(py.eval('1')).to.be(1);
|
||||
expect(py.eval('42')).to.be(42);
|
||||
expect(py.eval('9999')).to.be(9999);
|
||||
});
|
||||
it('should correctly handle negative literals', function () {
|
||||
expect(py.eval('-1')).to.be(-1);
|
||||
expect(py.eval('-42')).to.be(-42);
|
||||
expect(py.eval('-9999')).to.be(-9999);
|
||||
});
|
||||
it('should correctly handle float literals', function () {
|
||||
expect(py.eval('.42')).to.be(0.42);
|
||||
expect(py.eval('1.2')).to.be(1.2);
|
||||
});
|
||||
});
|
||||
describe('Booleans', function () {
|
||||
it('should have the right type', function () {
|
||||
expect(ev('False')).to.be.a(py.bool);
|
||||
expect(ev('True')).to.be.a(py.bool);
|
||||
});
|
||||
it('should yield the corresponding JS value', function () {
|
||||
expect(py.eval('False')).to.be(false);
|
||||
expect(py.eval('True')).to.be(true);
|
||||
});
|
||||
});
|
||||
describe('None', function () {
|
||||
it('should have the right type', function () {
|
||||
expect(ev('None')).to.be.a(py.object)
|
||||
});
|
||||
it('should yield a JS null', function () {
|
||||
expect(py.eval('None')).to.be(null);
|
||||
});
|
||||
});
|
||||
describe('String', function () {
|
||||
it('should have the right type', function () {
|
||||
expect(ev('"foo"')).to.be.a(py.str);
|
||||
expect(ev("'foo'")).to.be.a(py.str);
|
||||
});
|
||||
it('should yield the corresponding JS string', function () {
|
||||
expect(py.eval('"somestring"')).to.be('somestring');
|
||||
expect(py.eval("'somestring'")).to.be('somestring');
|
||||
});
|
||||
});
|
||||
describe('Tuple', function () {
|
||||
it('shoud have the right type', function () {
|
||||
expect(ev('()')).to.be.a(py.tuple);
|
||||
});
|
||||
it('should map to a JS array', function () {
|
||||
expect(py.eval('()')).to.eql([]);
|
||||
expect(py.eval('(1, 2, 3)')).to.eql([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
describe('List', function () {
|
||||
it('shoud have the right type', function () {
|
||||
expect(ev('[]')).to.be.a(py.list);
|
||||
});
|
||||
it('should map to a JS array', function () {
|
||||
expect(py.eval('[]')).to.eql([]);
|
||||
expect(py.eval('[1, 2, 3]')).to.eql([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
describe('Dict', function () {
|
||||
it('shoud have the right type', function () {
|
||||
expect(ev('{}')).to.be.a(py.dict);
|
||||
});
|
||||
it('should map to a JS object', function () {
|
||||
expect(py.eval("{}")).to.eql({});
|
||||
expect(py.eval("{'foo': 1, 'bar': 2}"))
|
||||
.to.eql({foo: 1, bar: 2});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Free variables', function () {
|
||||
it('should return its identity', function () {
|
||||
expect(py.eval('foo', {foo: 1})).to.be(1);
|
||||
expect(py.eval('foo', {foo: true})).to.be(true);
|
||||
expect(py.eval('foo', {foo: false})).to.be(false);
|
||||
expect(py.eval('foo', {foo: null})).to.be(null);
|
||||
expect(py.eval('foo', {foo: 'bar'})).to.be('bar');
|
||||
});
|
||||
});
|
||||
describe('Comparisons', function () {
|
||||
describe('equality', function () {
|
||||
it('should work with literals', function () {
|
||||
expect(py.eval('1 == 1')).to.be(true);
|
||||
expect(py.eval('"foo" == "foo"')).to.be(true);
|
||||
expect(py.eval('"foo" == "bar"')).to.be(false);
|
||||
});
|
||||
it('should work with free variables', function () {
|
||||
expect(py.eval('1 == a', {a: 1})).to.be(true);
|
||||
expect(py.eval('foo == "bar"', {foo: 'bar'})).to.be(true);
|
||||
expect(py.eval('foo == "bar"', {foo: 'qux'})).to.be(false);
|
||||
});
|
||||
});
|
||||
describe('inequality', function () {
|
||||
it('should work with literals', function () {
|
||||
expect(py.eval('1 != 2')).to.be(true);
|
||||
expect(py.eval('"foo" != "foo"')).to.be(false);
|
||||
expect(py.eval('"foo" != "bar"')).to.be(true);
|
||||
});
|
||||
it('should work with free variables', function () {
|
||||
expect(py.eval('1 != a', {a: 42})).to.be(true);
|
||||
expect(py.eval('foo != "bar"', {foo: 'bar'})).to.be(false);
|
||||
expect(py.eval('foo != "bar"', {foo: 'qux'})).to.be(true);
|
||||
expect(py.eval('foo != bar', {foo: 'qux', bar: 'quux'}))
|
||||
.to.be(true);
|
||||
});
|
||||
});
|
||||
describe('rich comparisons', function () {
|
||||
it('should work with numbers', function () {
|
||||
expect(py.eval('3 < 5')).to.be(true);
|
||||
expect(py.eval('5 >= 3')).to.be(true);
|
||||
expect(py.eval('3 >= 3')).to.be(true);
|
||||
expect(py.eval('3 > 5')).to.be(false);
|
||||
});
|
||||
it('should support comparison chains', function () {
|
||||
expect(py.eval('1 < 3 < 5')).to.be(true);
|
||||
expect(py.eval('5 > 3 > 1')).to.be(true);
|
||||
expect(py.eval('1 < 3 > 2 == 2 > -2')).to.be(true);
|
||||
});
|
||||
it('should compare strings', function () {
|
||||
expect(py.eval('date >= current',
|
||||
{date: '2010-06-08', current: '2010-06-05'}))
|
||||
.to.be(true);
|
||||
expect(py.eval('state == "cancel"', {state: 'cancel'}))
|
||||
.to.be(true);
|
||||
expect(py.eval('state == "cancel"', {state: 'open'}))
|
||||
.to.be(false);
|
||||
});
|
||||
});
|
||||
describe('missing eq/neq', function () {
|
||||
it('should fall back on identity', function () {
|
||||
var typ = new py.type(function MyType() {});
|
||||
expect(py.eval('MyType() == MyType()', {MyType: typ})).to.be(false);
|
||||
});
|
||||
});
|
||||
describe('un-comparable types', function () {
|
||||
it('should default to type-name ordering', function () {
|
||||
var t1 = new py.type(function Type1() {});
|
||||
var t2 = new py.type(function Type2() {});
|
||||
expect(py.eval('T1() < T2()', {T1: t1, T2: t2})).to.be(true);
|
||||
expect(py.eval('T1() > T2()', {T1: t1, T2: t2})).to.be(false);
|
||||
});
|
||||
it('should handle native stuff', function () {
|
||||
expect(py.eval('None < 42')).to.be(true);
|
||||
expect(py.eval('42 > None')).to.be(true);
|
||||
expect(py.eval('None > 42')).to.be(false);
|
||||
|
||||
expect(py.eval('None < False')).to.be(true);
|
||||
expect(py.eval('None < True')).to.be(true);
|
||||
expect(py.eval('False > None')).to.be(true);
|
||||
expect(py.eval('True > None')).to.be(true);
|
||||
expect(py.eval('None > False')).to.be(false);
|
||||
expect(py.eval('None > True')).to.be(false);
|
||||
|
||||
expect(py.eval('False < ""')).to.be(true);
|
||||
expect(py.eval('"" > False')).to.be(true);
|
||||
expect(py.eval('False > ""')).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Boolean operators', function () {
|
||||
it('should work', function () {
|
||||
expect(py.eval("foo == 'foo' or foo == 'bar'",
|
||||
{foo: 'bar'}))
|
||||
.to.be(true);;
|
||||
expect(py.eval("foo == 'foo' and bar == 'bar'",
|
||||
{foo: 'foo', bar: 'bar'}))
|
||||
.to.be(true);;
|
||||
});
|
||||
it('should be lazy', function () {
|
||||
// second clause should nameerror if evaluated
|
||||
expect(py.eval("foo == 'foo' or bar == 'bar'",
|
||||
{foo: 'foo'}))
|
||||
.to.be(true);;
|
||||
expect(py.eval("foo == 'foo' and bar == 'bar'",
|
||||
{foo: 'bar'}))
|
||||
.to.be(false);;
|
||||
});
|
||||
it('should return the actual object', function () {
|
||||
expect(py.eval('"foo" or "bar"')).to.be('foo');
|
||||
expect(py.eval('None or "bar"')).to.be('bar');
|
||||
expect(py.eval('False or None')).to.be(null);
|
||||
expect(py.eval('0 or 1')).to.be(1);
|
||||
});
|
||||
});
|
||||
describe('Containment', function () {
|
||||
describe('in sequences', function () {
|
||||
it('should match collection items', function () {
|
||||
expect(py.eval("'bar' in ('foo', 'bar')"))
|
||||
.to.be(true);
|
||||
expect(py.eval('1 in (1, 2, 3, 4)'))
|
||||
.to.be(true);;
|
||||
expect(py.eval('1 in (2, 3, 4)'))
|
||||
.to.be(false);;
|
||||
expect(py.eval('"url" in ("url",)'))
|
||||
.to.be(true);
|
||||
expect(py.eval('"foo" in ["foo", "bar"]'))
|
||||
.to.be(true);
|
||||
});
|
||||
it('should not be recursive', function () {
|
||||
expect(py.eval('"ur" in ("url",)'))
|
||||
.to.be(false);;
|
||||
});
|
||||
it('should be negatable', function () {
|
||||
expect(py.eval('1 not in (2, 3, 4)')).to.be(true);
|
||||
expect(py.eval('"ur" not in ("url",)')).to.be(true);
|
||||
expect(py.eval('-2 not in (1, 2, 3)')).to.be(true);
|
||||
});
|
||||
});
|
||||
describe('in dict', function () {
|
||||
// TODO
|
||||
});
|
||||
describe('in strings', function () {
|
||||
it('should match the whole string', function () {
|
||||
expect(py.eval('"view" in "view"')).to.be(true);
|
||||
expect(py.eval('"bob" in "view"')).to.be(false);
|
||||
});
|
||||
it('should match substrings', function () {
|
||||
expect(py.eval('"ur" in "url"')).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Conversions', function () {
|
||||
describe('to bool', function () {
|
||||
describe('strings', function () {
|
||||
it('should be true if non-empty', function () {
|
||||
expect(py.eval('bool(date_deadline)',
|
||||
{date_deadline: '2008'}))
|
||||
.to.be(true);
|
||||
});
|
||||
it('should be false if empty', function () {
|
||||
expect(py.eval('bool(s)', {s: ''})) .to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Attribute access', function () {
|
||||
it("should return the attribute's value", function () {
|
||||
var o = new py.object();
|
||||
o.bar = py.True;
|
||||
expect(py.eval('foo.bar', {foo: o})).to.be(true);
|
||||
o.bar = py.False;
|
||||
expect(py.eval('foo.bar', {foo: o})).to.be(false);
|
||||
});
|
||||
it("should work with functions", function () {
|
||||
var o = new py.object();
|
||||
o.bar = new py.def(function () {
|
||||
return new py.str("ok");
|
||||
});
|
||||
expect(py.eval('foo.bar()', {foo: o})).to.be('ok');
|
||||
});
|
||||
it('should not convert function attributes into methods', function () {
|
||||
var o = new py.object();
|
||||
o.bar = new py.type(function bar() {});
|
||||
o.bar.__getattribute__ = function () {
|
||||
return o.bar.baz;
|
||||
}
|
||||
o.bar.baz = py.True;
|
||||
expect(py.eval('foo.bar.baz', {foo: o})).to.be(true);
|
||||
});
|
||||
it('should work on instance attributes', function () {
|
||||
var typ = py.type(function MyType() {
|
||||
this.attr = new py.float(3);
|
||||
}, py.object, {});
|
||||
expect(py.eval('MyType().attr', {MyType: typ})).to.be(3);
|
||||
});
|
||||
it('should work on class attributes', function () {
|
||||
var typ = py.type(function MyType() {}, py.object, {
|
||||
attr: new py.float(3)
|
||||
});
|
||||
expect(py.eval('MyType().attr', {MyType: typ})).to.be(3);
|
||||
});
|
||||
it('should work with methods', function () {
|
||||
var typ = py.type(function MyType() {
|
||||
this.attr = new py.float(3);
|
||||
}, py.object, {
|
||||
some_method: function () { return new py.str('ok'); },
|
||||
get_attr: function () { return this.attr; }
|
||||
});
|
||||
expect(py.eval('MyType().some_method()', {MyType: typ})).to.be('ok');
|
||||
expect(py.eval('MyType().get_attr()', {MyType: typ})).to.be(3);
|
||||
});
|
||||
});
|
||||
describe('Callables', function () {
|
||||
it('should wrap JS functions', function () {
|
||||
expect(py.eval('foo()', {foo: function foo() { return new py.float(3); }}))
|
||||
.to.be(3);
|
||||
});
|
||||
it('should work on custom types', function () {
|
||||
var typ = py.type(function MyType() {}, py.object, {
|
||||
toJSON: function () { return true; }
|
||||
});
|
||||
expect(py.eval('MyType()', {MyType: typ})).to.be(true);
|
||||
});
|
||||
it('should accept kwargs', function () {
|
||||
expect(py.eval('foo(ok=True)', {
|
||||
foo: function foo() { return py.True; }
|
||||
})).to.be(true);
|
||||
});
|
||||
it('should be able to get its kwargs', function () {
|
||||
expect(py.eval('foo(ok=True)', {
|
||||
foo: function foo(args, kwargs) { return kwargs.ok; }
|
||||
})).to.be(true);
|
||||
});
|
||||
it('should be able to have both args and kwargs', function () {
|
||||
expect(py.eval('foo(1, 2, 3, ok=True, nok=False)', {
|
||||
foo: function (args, kwargs) {
|
||||
expect(args).to.have.length(3);
|
||||
expect(args[0].toJSON()).to.be(1);
|
||||
expect(kwargs).to.only.have.keys('ok', 'nok')
|
||||
expect(kwargs.nok.toJSON()).to.be(false);
|
||||
return kwargs.ok;
|
||||
}
|
||||
})).to.be(true);
|
||||
});
|
||||
});
|
||||
describe('issubclass', function () {
|
||||
it('should say a type is its own subclass', function () {
|
||||
expect(py.issubclass.__call__([py.dict, py.dict]).toJSON())
|
||||
.to.be(true);
|
||||
expect(py.eval('issubclass(dict, dict)'))
|
||||
.to.be(true);
|
||||
});
|
||||
it('should work with subtypes', function () {
|
||||
expect(py.issubclass.__call__([py.bool, py.object]).toJSON())
|
||||
.to.be(true);
|
||||
});
|
||||
});
|
||||
describe('builtins', function () {
|
||||
it('should aways be available', function () {
|
||||
expect(py.eval('bool("foo")')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('numerical protocols', function () {
|
||||
describe('True numbers (float)', function () {
|
||||
describe('Basic arithmetic', function () {
|
||||
it('can be added', function () {
|
||||
expect(py.eval('1 + 1')).to.be(2);
|
||||
expect(py.eval('1.5 + 2')).to.be(3.5);
|
||||
expect(py.eval('1 + -1')).to.be(0);
|
||||
});
|
||||
it('can be subtracted', function () {
|
||||
expect(py.eval('1 - 1')).to.be(0);
|
||||
expect(py.eval('1.5 - 2')).to.be(-0.5);
|
||||
expect(py.eval('2 - 1.5')).to.be(0.5);
|
||||
});
|
||||
it('can be multiplied', function () {
|
||||
expect(py.eval('1 * 3')).to.be(3);
|
||||
expect(py.eval('0 * 5')).to.be(0);
|
||||
expect(py.eval('42 * -2')).to.be(-84);
|
||||
});
|
||||
it('can be divided', function () {
|
||||
expect(py.eval('1 / 2')).to.be(0.5);
|
||||
expect(py.eval('2 / 1')).to.be(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Strings', function () {
|
||||
describe('Basic arithmetics operators', function () {
|
||||
it('can be added (concatenation)', function () {
|
||||
expect(py.eval('"foo" + "bar"')).to.be('foobar');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: 87fb1b67d6a13f10a1a328104ee4d4b2c36801ec
|
||||
branch: default
|
||||
latesttag: 0.2
|
||||
latesttagdistance: 1
|
|
@ -1 +0,0 @@
|
|||
Parser and evaluator of Python expressions
|
|
@ -1,14 +0,0 @@
|
|||
* 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)
|
|
@ -1,546 +0,0 @@
|
|||
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 (!(character == '.' || 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);
|
|
@ -1,90 +0,0 @@
|
|||
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'}));;
|
|
@ -0,0 +1,330 @@
|
|||
// Variables {{{
|
||||
$colour4: #8a89ba
|
||||
//$colour4: #d14836
|
||||
// }}}
|
||||
|
||||
// Mixins {{{
|
||||
@mixin vertical-gradient($startColor: #555, $endColor: #333)
|
||||
background-color: $startColor
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */
|
||||
background-image: -webkit-linear-gradient(top, $startColor, $endColor) /* Chrome 10+, Saf5.1+, iOS 5+ */
|
||||
background-image: -moz-linear-gradient(top, $startColor, $endColor) /* FF3.6 */
|
||||
background-image: -ms-linear-gradient(top, $startColor, $endColor) /* IE10 */
|
||||
background-image: -o-linear-gradient(top, $startColor, $endColor) /* Opera 11.10+ */
|
||||
background-image: linear-gradient(to bottom, $startColor, $endColor)
|
||||
|
||||
|
||||
@mixin radial-gradient($gradient)
|
||||
background-position: center center
|
||||
background-image: -webkit-radial-gradient(circle, $gradient)
|
||||
background-image: -moz-radial-gradient($gradient)
|
||||
background-image: -ms-radial-gradient($gradient)
|
||||
background-image: radial-gradient($gradient)
|
||||
|
||||
@mixin radius($radius: 5px)
|
||||
-moz-border-radius: $radius
|
||||
-webkit-border-radius: $radius
|
||||
border-radius: $radius
|
||||
|
||||
@mixin box-shadow($bsval: 0px 1px 4px #777)
|
||||
-moz-box-shadow: $bsval
|
||||
-webkit-box-shadow: $bsval
|
||||
-box-shadow: $bsval
|
||||
|
||||
@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s))
|
||||
-webkit-transition: $transval
|
||||
-moz-transition: $transval
|
||||
-ms-transition: $transval
|
||||
-o-transition: $transval
|
||||
transition: $transval
|
||||
|
||||
@mixin opacity($opacity: .5)
|
||||
filter: alpha(opacity=$opacity * 100)
|
||||
opacity: $opacity
|
||||
|
||||
@mixin background-clip($clip: padding-box)
|
||||
-webkit-background-clip: $clip
|
||||
-moz-background-clip: $clip
|
||||
background-clip: $clip
|
||||
// }}}
|
||||
|
||||
.openerp2
|
||||
// Common styles {{{
|
||||
padding: 0
|
||||
margin: 0
|
||||
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
|
||||
color: #4c4c4c
|
||||
font-size: 13px
|
||||
background: white
|
||||
position: relative
|
||||
|
||||
a
|
||||
text-decoration: none
|
||||
// }}}
|
||||
|
||||
// WebClient {{{
|
||||
.oe_webclient
|
||||
position: absolute
|
||||
top: 0
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
.oe_application
|
||||
position: absolute
|
||||
top: 32px
|
||||
bottom: 0
|
||||
left: 206px
|
||||
right: 0
|
||||
// }}}
|
||||
|
||||
// Fullscreen mode {{{
|
||||
.oe_content_full_screen
|
||||
.oe_application
|
||||
top: 0
|
||||
left: 0
|
||||
.topbar, .leftbar
|
||||
display: none
|
||||
// }}
|
||||
|
||||
// Topbar {{{
|
||||
.oe_topbar
|
||||
width: 100%
|
||||
height: 31px
|
||||
border-top: solid 1px #d3d3d3
|
||||
border-bottom: solid 1px black
|
||||
@include vertical-gradient(#646060, #262626)
|
||||
|
||||
.oe_systray
|
||||
float: right
|
||||
.oe_systray > div
|
||||
float: left
|
||||
padding: 0 4px 0 4px
|
||||
|
||||
.oe_topbar_item
|
||||
li
|
||||
float: left
|
||||
a
|
||||
display: block
|
||||
padding: 5px 10px 7px
|
||||
line-height: 20px
|
||||
height: 20px
|
||||
color: #eee
|
||||
vertical-align: top
|
||||
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
|
||||
&:hover
|
||||
background: #303030
|
||||
color: white
|
||||
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
|
||||
.oe_active
|
||||
background: #303030
|
||||
font-weight: bold
|
||||
color: white
|
||||
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
|
||||
|
||||
.oe_topbar_avatar
|
||||
width: 24px
|
||||
height: 24px
|
||||
margin: -2px 2px 0 0
|
||||
@include radius(4px)
|
||||
.oe_topbar_avatar
|
||||
vertical-align: top
|
||||
// }}}
|
||||
|
||||
// Leftbar {{{
|
||||
.oe_leftbar
|
||||
width: 205px
|
||||
height: 100%
|
||||
background: #f0eeee
|
||||
border-right: 1px solid #afafb6
|
||||
overflow: auto
|
||||
text-shadow: 0 1px 1px white
|
||||
|
||||
.oe_footer
|
||||
position: absolute
|
||||
width: 205px
|
||||
text-align: center
|
||||
bottom: 8px
|
||||
a.oe_logo
|
||||
display: block
|
||||
text-align: center
|
||||
height: 70px
|
||||
line-height: 70px
|
||||
img
|
||||
height: 40px
|
||||
width: 157px
|
||||
margin: 14px 0
|
||||
.oe_footer
|
||||
position: absolute
|
||||
width: 205px
|
||||
text-align: center
|
||||
bottom: 8px
|
||||
a
|
||||
font-weight: 800
|
||||
font-family: serif
|
||||
font-size: 16px
|
||||
color: black
|
||||
span
|
||||
color: #c81010
|
||||
font-style: italic
|
||||
// }}}
|
||||
|
||||
// Menu {{{
|
||||
.oe_menu
|
||||
float: left
|
||||
padding: 0
|
||||
margin: 0
|
||||
li
|
||||
list-style-type: none
|
||||
float: left
|
||||
a
|
||||
display: block
|
||||
padding: 5px 10px 7px
|
||||
line-height: 20px
|
||||
height: 20px
|
||||
color: #eee
|
||||
vertical-align: top
|
||||
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
|
||||
&:hover
|
||||
background: #303030
|
||||
color: white
|
||||
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
|
||||
.oe_active
|
||||
background: #303030
|
||||
font-weight: bold
|
||||
color: white
|
||||
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
|
||||
.oe_secondary_menu_section
|
||||
font-weight: bold
|
||||
margin-left: 8px
|
||||
color: $colour4
|
||||
.oe_secondary_submenu
|
||||
padding: 2px 0 8px 0
|
||||
margin: 0
|
||||
width: 100%
|
||||
display: inline-block
|
||||
li
|
||||
position: relative
|
||||
padding: 1px 0 1px 16px
|
||||
list-style-type: none
|
||||
a
|
||||
display: block
|
||||
color: #4c4c4c
|
||||
padding: 2px 4px 2px 0
|
||||
.oe_menu_label
|
||||
position: absolute
|
||||
top: 1px
|
||||
right: 1px
|
||||
font-size: 10px
|
||||
background: $colour4
|
||||
color: white
|
||||
padding: 2px 4px
|
||||
margin: 1px 6px 0 0
|
||||
border: 1px solid lightGray
|
||||
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
|
||||
@include radius(4px)
|
||||
@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2))
|
||||
.oe_active
|
||||
background: $colour4
|
||||
border-top: 1px solid lightGray
|
||||
border-bottom: 1px solid lightGray
|
||||
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
|
||||
@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2))
|
||||
a
|
||||
color: white
|
||||
.oe_menu_label
|
||||
background: #eee
|
||||
color: $colour4
|
||||
text-shadow: 0 1px 1px white
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2))
|
||||
.oe_menu_toggler:before
|
||||
width: 0
|
||||
height: 0
|
||||
display: inline-block
|
||||
content: "&darr"
|
||||
text-indent: -99999px
|
||||
vertical-align: top
|
||||
margin-left: -8px
|
||||
margin-top: 4px
|
||||
margin-right: 4px
|
||||
border-top: 4px solid transparent
|
||||
border-bottom: 4px solid transparent
|
||||
border-left: 4px solid #4c4c4c
|
||||
@include opacity(0.5)
|
||||
.oe_menu_opened:before
|
||||
margin-top: 6px
|
||||
margin-left: -12px
|
||||
margin-right: 4px
|
||||
border-left: 4px solid transparent
|
||||
border-right: 4px solid transparent
|
||||
border-top: 4px solid #4c4c4c
|
||||
// }}}
|
||||
|
||||
// UserMenu {{{
|
||||
.oe_user_menu
|
||||
float: right
|
||||
padding: 0
|
||||
margin: 0
|
||||
li
|
||||
list-style-type: none
|
||||
float: left
|
||||
.oe_dropdown
|
||||
position: relative
|
||||
|
||||
.oe_dropdown_toggle:after
|
||||
width: 0
|
||||
height: 0
|
||||
display: inline-block
|
||||
content: "&darr"
|
||||
text-indent: -99999px
|
||||
vertical-align: top
|
||||
margin-top: 8px
|
||||
margin-left: 4px
|
||||
border-left: 4px solid transparent
|
||||
border-right: 4px solid transparent
|
||||
border-top: 4px solid white
|
||||
@include opacity(0.5)
|
||||
|
||||
.oe_dropdown_options
|
||||
float: left
|
||||
background: #333
|
||||
background: rgba(37,37,37,0.9)
|
||||
display: none
|
||||
position: absolute
|
||||
top: 32px
|
||||
right: -1px
|
||||
border: 0
|
||||
z-index: 900
|
||||
margin-left: 0
|
||||
margin-right: 0
|
||||
padding: 6px 0
|
||||
zoom: 1
|
||||
border-color: #999
|
||||
border-color: rgba(0, 0, 0, 0.2)
|
||||
border-style: solid
|
||||
border-width: 0 1px 1px
|
||||
@include radius(0 0 6px 6px)
|
||||
@include box-shadow(0 1px 4px rgba(0,0,0,0.3))
|
||||
@include background-clip()
|
||||
li
|
||||
float: none
|
||||
display: block
|
||||
background-color: none
|
||||
a
|
||||
display: block
|
||||
padding: 4px 15px
|
||||
clear: both
|
||||
font-weight: normal
|
||||
line-height: 18px
|
||||
color: #eee
|
||||
&:hover
|
||||
@include vertical-gradient(#292929, #191919)
|
||||
@include box-shadow(none)
|
||||
// }}}
|
||||
|
||||
.openerp
|
||||
// Transitional overrides for old styles {{{
|
||||
// }}}
|
||||
|
||||
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
|
||||
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
|
@ -0,0 +1,6 @@
|
|||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
}
|
Before Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 149 B |
Before Width: | Height: | Size: 454 B |
Before Width: | Height: | Size: 452 B |
Before Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 353 B |
After Width: | Height: | Size: 846 B |
|
@ -45,6 +45,12 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.
|
|||
|
||||
});
|
||||
|
||||
openerp.web.dialog = function(element) {
|
||||
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
|
||||
result.dialog("widget").addClass("openerp");
|
||||
return result;
|
||||
}
|
||||
|
||||
openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{
|
||||
dialog_title: "",
|
||||
/**
|
||||
|
@ -86,7 +92,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
|
|||
if (this.dialog_options.autoOpen) {
|
||||
this.open();
|
||||
} else {
|
||||
this.$element.dialog(this.get_options());
|
||||
openerp.web.dialog(this.$element, this.get_options());
|
||||
}
|
||||
},
|
||||
get_options: function(options) {
|
||||
|
@ -125,7 +131,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
|
|||
this.$element.html(this.render());
|
||||
}
|
||||
var o = this.get_options(options);
|
||||
this.$element.dialog(o).dialog('open');
|
||||
openerp.web.dialog(this.$element, o).dialog('open');
|
||||
if (o.height === 'auto' && o.max_height) {
|
||||
this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
|
||||
}
|
||||
|
@ -142,7 +148,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
|
|||
on_resized: function() {
|
||||
//openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height());
|
||||
},
|
||||
stop: function () {
|
||||
destroy: function () {
|
||||
// Destroy widget
|
||||
this.close();
|
||||
this.$element.dialog('destroy');
|
||||
|
@ -166,7 +172,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
|
|||
}
|
||||
},
|
||||
on_managed_error: function(error) {
|
||||
$('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>').dialog({
|
||||
openerp.web.dialog($('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>'), {
|
||||
title: "OpenERP " + _.str.capitalize(error.type),
|
||||
buttons: [
|
||||
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
|
||||
|
@ -234,7 +240,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
|
|||
this.session.on_rpc_request.add_first(this.request_call);
|
||||
this.session.on_rpc_response.add_last(this.response_call);
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
this.session.on_rpc_request.remove(this.request_call);
|
||||
this.session.on_rpc_response.remove(this.response_call);
|
||||
this.on_rpc_event(-this.count);
|
||||
|
@ -255,7 +261,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
|
|||
$(".loading",this.$element).text(_.str.sprintf(
|
||||
_t("Loading (%d)"), this.count));
|
||||
$(".loading",this.$element).show();
|
||||
this.widget_parent.$element.addClass('loading');
|
||||
this.getParent().$element.addClass('loading');
|
||||
} else {
|
||||
this.count = 0;
|
||||
clearTimeout(this.long_running_timer);
|
||||
|
@ -265,7 +271,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
|
|||
$.unblockUI();
|
||||
}
|
||||
$(".loading",this.$element).fadeOut();
|
||||
this.widget_parent.$element.removeClass('loading');
|
||||
this.getParent().$element.removeClass('loading');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -312,7 +318,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
self.hide();
|
||||
});
|
||||
},
|
||||
stop: function () {
|
||||
destroy: function () {
|
||||
this.hide();
|
||||
this.$option_id.empty();
|
||||
|
||||
|
@ -344,44 +350,6 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
});
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Waits until the new database is done creating, then unblocks the UI and
|
||||
* logs the user in as admin
|
||||
*
|
||||
* @param {Number} db_creation_id identifier for the db-creation operation, used to fetch the current installation progress
|
||||
* @param {Object} info info fields for this database creation
|
||||
* @param {String} info.db name of the database being created
|
||||
* @param {String} info.password super-admin password for the database
|
||||
*/
|
||||
wait_for_newdb: function (db_creation_id, info) {
|
||||
var self = this;
|
||||
self.rpc('/web/database/progress', {
|
||||
id: db_creation_id,
|
||||
password: info.password
|
||||
}, function (result) {
|
||||
var progress = result[0];
|
||||
// I'd display a progress bar, but turns out the progress status
|
||||
// the server report kind-of blows goats: it's at 0 for ~75% of
|
||||
// the installation, then jumps to 75%, then jumps down to either
|
||||
// 0 or ~40%, then back up to 75%, then terminates. Let's keep that
|
||||
// mess hidden behind a not-very-useful but not overly weird
|
||||
// message instead.
|
||||
if (progress < 1) {
|
||||
setTimeout(function () {
|
||||
self.wait_for_newdb(db_creation_id, info);
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
var admin = result[1][0];
|
||||
setTimeout(function () {
|
||||
self.widget_parent.do_login(
|
||||
info.db, admin.login, admin.password);
|
||||
self.stop();
|
||||
self.unblockUI();
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Blocks UI and replaces $.unblockUI by a noop to prevent third parties
|
||||
* from unblocking the UI
|
||||
|
@ -407,7 +375,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
* @param {String} error.error message of the error dialog
|
||||
*/
|
||||
display_error: function (error) {
|
||||
return $('<div>').dialog({
|
||||
return openerp.web.dialog($('<div>'), {
|
||||
modal: true,
|
||||
title: error.title,
|
||||
buttons: [
|
||||
|
@ -421,23 +389,19 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
self.$option_id.find("form[name=create_db_form]").validate({
|
||||
submitHandler: function (form) {
|
||||
var fields = $(form).serializeArray();
|
||||
self.blockUI();
|
||||
self.rpc("/web/database/create", {'fields': fields}, function(result) {
|
||||
if (result.error) {
|
||||
self.unblockUI();
|
||||
self.display_error(result);
|
||||
return;
|
||||
}
|
||||
if (self.db_list) {
|
||||
self.db_list.push(self.to_object(fields)['db_name']);
|
||||
self.db_list.sort();
|
||||
self.widget_parent.set_db_list(self.db_list);
|
||||
}
|
||||
|
||||
var form_obj = self.to_object(fields);
|
||||
self.wait_for_newdb(result, {
|
||||
password: form_obj['super_admin_pwd'],
|
||||
db: form_obj['db_name']
|
||||
});
|
||||
self.getParent().do_login(
|
||||
form_obj['db_name'],
|
||||
'admin',
|
||||
form_obj['create_admin_pwd']);
|
||||
self.destroy();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -449,7 +413,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
submitHandler: function (form) {
|
||||
var $form = $(form),
|
||||
fields = $form.serializeArray(),
|
||||
$db_list = $form.find('select[name=drop_db]'),
|
||||
$db_list = $form.find('[name=drop_db]'),
|
||||
db = $db_list.val();
|
||||
|
||||
if (!confirm("Do you really want to delete the database: " + db + " ?")) {
|
||||
|
@ -463,7 +427,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
|
|||
$db_list.find(':selected').remove();
|
||||
if (self.db_list) {
|
||||
self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
|
||||
self.widget_parent.set_db_list(self.db_list);
|
||||
self.getParent().set_db_list(self.db_list);
|
||||
}
|
||||
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
|
||||
});
|
||||
|
@ -660,58 +624,212 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
|
|||
}
|
||||
});
|
||||
|
||||
openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header# */{
|
||||
template: "Header",
|
||||
openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
|
||||
/**
|
||||
* @constructs openerp.web.Header
|
||||
* @extends openerp.web.OldWidget
|
||||
* @constructs openerp.web.Menu
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
template: 'Menu',
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.has_been_loaded = $.Deferred();
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$secondary_menus = this.getParent().$element.find('.oe_secondary_menus_container');
|
||||
this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
|
||||
},
|
||||
do_reload: function() {
|
||||
var self = this;
|
||||
return this.rpc("/web/menu/load", {}, this.on_loaded).then(function () {
|
||||
if (self.current_menu) {
|
||||
self.open_menu(self.current_menu);
|
||||
}
|
||||
});
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.data = data;
|
||||
this.renderElement();
|
||||
this.$element.on('click', 'a[data-menu]', this.on_menu_click);
|
||||
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
|
||||
// Hide second level submenus
|
||||
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
|
||||
this.has_been_loaded.resolve();
|
||||
},
|
||||
/**
|
||||
* Opens a given menu by id, as if a user had browsed to that menu by hand
|
||||
* except does not trigger any event on the way
|
||||
*
|
||||
* @param {Number} id database id of the terminal menu to select
|
||||
*/
|
||||
open_menu: function (id) {
|
||||
var $clicked_menu, $sub_menu, $main_menu;
|
||||
$clicked_menu = this.$element.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
|
||||
|
||||
if (this.$secondary_menus.has($clicked_menu).length) {
|
||||
$sub_menu = $clicked_menu.parents('.oe_secondary_menu');
|
||||
$main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
|
||||
} else {
|
||||
$sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
|
||||
$main_menu = $clicked_menu;
|
||||
}
|
||||
|
||||
// Activate current main menu
|
||||
this.$element.find('.oe_active').removeClass('oe_active');
|
||||
$main_menu.addClass('oe_active');
|
||||
|
||||
// Show current sub menu
|
||||
this.$secondary_menus.find('.oe_secondary_menu').hide();
|
||||
$sub_menu.show();
|
||||
|
||||
// Activate current menu item and show parents
|
||||
this.$secondary_menus.find('.oe_active').removeClass('oe_active');
|
||||
if ($main_menu !== $clicked_menu) {
|
||||
$clicked_menu.parents().show();
|
||||
if ($clicked_menu.is('.oe_menu_toggler')) {
|
||||
$clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggle();
|
||||
} else {
|
||||
$clicked_menu.parent().addClass('oe_active');
|
||||
}
|
||||
}
|
||||
},
|
||||
open_action: function (id) {
|
||||
var menu_id, $menu = this.$element.add(this.$secondary_menus).find('a[data-action-id=' + id + ']');
|
||||
if (menu_id = $menu.data('menu')) {
|
||||
this.open_menu(menu_id);
|
||||
}
|
||||
},
|
||||
on_menu_click: function(ev, id) {
|
||||
id = id || 0;
|
||||
var $clicked_menu, manual = false;
|
||||
|
||||
if (id) {
|
||||
// We can manually activate a menu with it's id (for hash url mapping)
|
||||
manual = true;
|
||||
$clicked_menu = this.$element.find('a[data-menu=' + id + ']');
|
||||
if (!$clicked_menu.length) {
|
||||
$clicked_menu = this.$secondary_menus.find('a[data-menu=' + id + ']');
|
||||
}
|
||||
} else {
|
||||
$clicked_menu = $(ev.currentTarget);
|
||||
id = $clicked_menu.data('menu');
|
||||
}
|
||||
|
||||
this.trigger('menuClicked', id, $clicked_menu);
|
||||
|
||||
if (id) {
|
||||
this.open_menu(id);
|
||||
this.current_menu = id;
|
||||
this.session.active_id = id;
|
||||
var action_id = $clicked_menu.data('action-id');
|
||||
if (action_id) {
|
||||
this.on_action(action_id);
|
||||
}
|
||||
}
|
||||
if (ev) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
do_show_secondary: function($sub_menu, $main_menu) {
|
||||
var self = this;
|
||||
this.$secondary_menus.show();
|
||||
if (!arguments.length) {
|
||||
return;
|
||||
}
|
||||
$sub_menu.show();
|
||||
},
|
||||
on_action: function(action) {
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMenu# */{
|
||||
template: "UserMenu",
|
||||
/**
|
||||
* @constructs openerp.web.UserMenu
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
init: function(parent) {
|
||||
this._super(parent);
|
||||
this.qs = "?" + jQuery.param.querystring();
|
||||
this.$content = $();
|
||||
this.update_promise = $.Deferred().resolve();
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
$('html').bind('click', function() {
|
||||
self.$element.find('.oe_dropdown_options').hide();
|
||||
});
|
||||
this.$element.find('.oe_dropdown_toggle').click(function() {
|
||||
self.$element.find('.oe_dropdown_options').toggle();
|
||||
return false;
|
||||
});
|
||||
this.$element.on('click', '.oe_dropdown_options li a[data-menu]', function() {
|
||||
var f = self['on_menu_' + $(this).data('menu')];
|
||||
f && f($(this));
|
||||
self.$element.find('.oe_dropdown_options').hide();
|
||||
return false;
|
||||
});
|
||||
},
|
||||
change_password :function() {
|
||||
var self = this;
|
||||
this.dialog = new openerp.web.Dialog(this, {
|
||||
title: _t("Change Password"),
|
||||
width : 'auto'
|
||||
}).open();
|
||||
this.dialog.$element.html(QWeb.render("Change_Pwd", self));
|
||||
this.dialog.$element.find("form[name=change_password_form]").validate({
|
||||
submitHandler: function (form) {
|
||||
self.rpc("/web/session/change_password",{
|
||||
'fields': $(form).serializeArray()
|
||||
}, function(result) {
|
||||
if (result.error) {
|
||||
self.display_error(result);
|
||||
return;
|
||||
} else {
|
||||
openerp.webclient.on_logout();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
display_error: function (error) {
|
||||
return openerp.web.dialog($('<div>'), {
|
||||
modal: true,
|
||||
title: error.title,
|
||||
buttons: [
|
||||
{text: _("Ok"), click: function() { $(this).dialog("close"); }}
|
||||
]
|
||||
}).html(error.error);
|
||||
},
|
||||
do_update: function () {
|
||||
var self = this;
|
||||
var fct = function() {
|
||||
self.$content.remove();
|
||||
var $avatar = self.$element.find('.oe_topbar_avatar');
|
||||
$avatar.attr('src', $avatar.data('default-src'));
|
||||
if (!self.session.uid)
|
||||
return;
|
||||
var func = new openerp.web.Model("res.users").get_func("read");
|
||||
return func(self.session.uid, ["name", "company_id"]).pipe(function(res) {
|
||||
self.$content = $(QWeb.render("Header-content", {widget: self, user: res}));
|
||||
self.$content.appendTo(self.$element);
|
||||
self.$element.find(".logout").click(self.on_logout);
|
||||
self.$element.find("a.preferences").click(self.on_preferences);
|
||||
self.$element.find(".about").click(self.on_about);
|
||||
// TODO: Only show company if multicompany in use
|
||||
self.$element.find('.oe_topbar_name').text(res.name + '/' + res.company_id[1]);
|
||||
return self.shortcut_load();
|
||||
});
|
||||
};
|
||||
this.update_promise = this.update_promise.pipe(fct, fct);
|
||||
},
|
||||
on_about: function() {
|
||||
var self = this;
|
||||
self.rpc("/web/webclient/version_info", {}).then(function(res) {
|
||||
var $help = $(QWeb.render("About-Page", {version_info: res}));
|
||||
$help.find('a.oe_activate_debug_mode').click(function (e) {
|
||||
e.preventDefault();
|
||||
window.location = $.param.querystring(
|
||||
window.location.href, 'debug');
|
||||
});
|
||||
$help.dialog({autoOpen: true,
|
||||
modal: true, width: 960, title: _t("About")});
|
||||
});
|
||||
on_action: function() {
|
||||
},
|
||||
shortcut_load :function(){
|
||||
var self = this,
|
||||
sc = self.session.shortcuts,
|
||||
shortcuts_ds = new openerp.web.DataSet(this, 'ir.ui.view_sc');
|
||||
self.$element.find('.oe_dropdown_options a[data-menu=shortcut]').each(function() {
|
||||
$(this).parent().remove();
|
||||
});
|
||||
// TODO: better way to communicate between sections.
|
||||
// sc.bindings, because jquery does not bind/trigger on arrays...
|
||||
if (!sc.binding) {
|
||||
|
@ -719,52 +837,41 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
|
|||
$(sc.binding).bind({
|
||||
'add': function (e, attrs) {
|
||||
shortcuts_ds.create(attrs, function (out) {
|
||||
$('<li>', {
|
||||
'data-shortcut-id':out.result,
|
||||
'data-id': attrs.res_id
|
||||
}).text(attrs.name)
|
||||
.appendTo(self.$element.find('.oe-shortcuts ul'));
|
||||
var shortcut = QWeb.render('UserMenu.shortcut', {
|
||||
shortcuts : [{
|
||||
name : attrs.name,
|
||||
id : out.result,
|
||||
res_id : attrs.res_id
|
||||
}]
|
||||
});
|
||||
$(shortcut).appendTo(self.$element.find('.oe_dropdown_options'));
|
||||
attrs.id = out.result;
|
||||
sc.push(attrs);
|
||||
});
|
||||
},
|
||||
'remove-current': function () {
|
||||
var menu_id = self.session.active_id;
|
||||
var $shortcut = self.$element
|
||||
.find('.oe-shortcuts li[data-id=' + menu_id + ']');
|
||||
var $shortcut = self.$element.find('.oe_dropdown_options li a[data-id=' + menu_id + ']');
|
||||
var shortcut_id = $shortcut.data('shortcut-id');
|
||||
$shortcut.remove();
|
||||
shortcuts_ds.unlink([shortcut_id]);
|
||||
var sc_new = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id});
|
||||
sc.splice(0, sc.length);
|
||||
sc.push.apply(sc, sc_new);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.rpc('/web/session/sc_list', {}, function(shortcuts) {
|
||||
sc.splice(0, sc.length);
|
||||
sc.push.apply(sc, shortcuts);
|
||||
|
||||
self.$element.find('.oe-shortcuts')
|
||||
.html(QWeb.render('Shortcuts', {'shortcuts': shortcuts}))
|
||||
.undelegate('li', 'click')
|
||||
|
||||
.delegate('li', 'click', function(e) {
|
||||
e.stopPropagation();
|
||||
var id = $(this).data('id');
|
||||
self.session.active_id = id;
|
||||
self.rpc('/web/menu/action', {'menu_id':id}, function(ir_menu_data) {
|
||||
if (ir_menu_data.action.length){
|
||||
self.on_action(ir_menu_data.action[0][2]);
|
||||
}
|
||||
});
|
||||
});
|
||||
$(QWeb.render('UserMenu.shortcut', {'shortcuts': shortcuts}))
|
||||
.appendTo(self.$element.find('.oe_dropdown_options'));
|
||||
});
|
||||
},
|
||||
|
||||
on_action: function(action) {
|
||||
on_menu_logout: function() {
|
||||
},
|
||||
on_preferences: function(){
|
||||
on_menu_settings: function() {
|
||||
var self = this;
|
||||
var action_manager = new openerp.web.ActionManager(this);
|
||||
var dataset = new openerp.web.DataSet (this,'res.users',this.context);
|
||||
|
@ -793,7 +900,7 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
|
|||
var inner_viewmanager = action_manager.inner_viewmanager;
|
||||
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
|
||||
.then(function() {
|
||||
self.dialog.stop();
|
||||
self.dialog.destroy();
|
||||
// needs to refresh interface in case language changed
|
||||
window.location.reload();
|
||||
});
|
||||
|
@ -804,298 +911,66 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
|
|||
action_manager.appendTo(this.dialog);
|
||||
action_manager.render(this.dialog);
|
||||
},
|
||||
|
||||
change_password :function() {
|
||||
on_menu_about: function() {
|
||||
var self = this;
|
||||
this.dialog = new openerp.web.Dialog(this, {
|
||||
title: _t("Change Password"),
|
||||
width : 'auto'
|
||||
}).open();
|
||||
this.dialog.$element.html(QWeb.render("Change_Pwd", self));
|
||||
this.dialog.$element.find("form[name=change_password_form]").validate({
|
||||
submitHandler: function (form) {
|
||||
self.rpc("/web/session/change_password",{
|
||||
'fields': $(form).serializeArray()
|
||||
}, function(result) {
|
||||
if (result.error) {
|
||||
self.display_error(result);
|
||||
return;
|
||||
} else {
|
||||
openerp.webclient.on_logout();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
display_error: function (error) {
|
||||
return $('<div>').dialog({
|
||||
modal: true,
|
||||
title: error.title,
|
||||
buttons: [
|
||||
{text: _("Ok"), click: function() { $(this).dialog("close"); }}
|
||||
]
|
||||
}).html(error.error);
|
||||
},
|
||||
on_logout: function() {
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */{
|
||||
/**
|
||||
* @constructs openerp.web.Menu
|
||||
* @extends openerp.web.OldWidget
|
||||
*
|
||||
* @param parent
|
||||
* @param element_id
|
||||
* @param secondary_menu_id
|
||||
*/
|
||||
init: function(parent, element_id, secondary_menu_id) {
|
||||
this._super(parent, element_id);
|
||||
this.secondary_menu_id = secondary_menu_id;
|
||||
this.$secondary_menu = $("#" + secondary_menu_id);
|
||||
this.menu = false;
|
||||
this.folded = false;
|
||||
if (window.localStorage) {
|
||||
this.folded = localStorage.getItem('oe_menu_folded') === 'true';
|
||||
}
|
||||
this.float_timeout = 700;
|
||||
},
|
||||
start: function() {
|
||||
this.$secondary_menu.addClass(this.folded ? 'oe_folded' : 'oe_unfolded');
|
||||
},
|
||||
do_reload: function() {
|
||||
var self = this;
|
||||
return this.rpc("/web/menu/load", {}, this.on_loaded).then(function () {
|
||||
if (self.current_menu) {
|
||||
self.open_menu(self.current_menu);
|
||||
}
|
||||
});
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.data = data;
|
||||
this.$element.html(QWeb.render("Menu", { widget : this }));
|
||||
this.$secondary_menu.html(QWeb.render("Menu.secondary", { widget : this }));
|
||||
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
|
||||
this.$secondary_menu.find('.oe_toggle_secondary_menu').click(this.on_toggle_fold);
|
||||
},
|
||||
on_toggle_fold: function() {
|
||||
this.$secondary_menu.toggleClass('oe_folded').toggleClass('oe_unfolded');
|
||||
if (this.folded) {
|
||||
this.$secondary_menu.find('.oe_secondary_menu.active').show();
|
||||
} else {
|
||||
this.$secondary_menu.find('.oe_secondary_menu').hide();
|
||||
}
|
||||
this.folded = !this.folded;
|
||||
if (window.localStorage) {
|
||||
localStorage.setItem('oe_menu_folded', this.folded.toString());
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Opens a given menu by id, as if a user had browsed to that menu by hand
|
||||
* except does not trigger any event on the way
|
||||
*
|
||||
* @param {Number} menu_id database id of the terminal menu to select
|
||||
*/
|
||||
open_menu: function (menu_id) {
|
||||
this.$element.add(this.$secondary_menu).find('.active')
|
||||
.removeClass('active');
|
||||
this.$secondary_menu.find('> .oe_secondary_menu').hide();
|
||||
|
||||
var $primary_menu;
|
||||
var $secondary_submenu = this.$secondary_menu.find(
|
||||
'a[data-menu=' + menu_id +']');
|
||||
if ($secondary_submenu.length) {
|
||||
for(;;) {
|
||||
if ($secondary_submenu.hasClass('leaf')) {
|
||||
$secondary_submenu.addClass('active');
|
||||
} else if ($secondary_submenu.hasClass('submenu')) {
|
||||
$secondary_submenu.addClass('opened')
|
||||
}
|
||||
var $parent = $secondary_submenu.parent().show();
|
||||
if ($parent.hasClass('oe_secondary_menu')) {
|
||||
var primary_id = $parent.data('menu-parent');
|
||||
$primary_menu = this.$element.find(
|
||||
'a[data-menu=' + primary_id + ']');
|
||||
break;
|
||||
}
|
||||
$secondary_submenu = $parent.prev();
|
||||
}
|
||||
} else {
|
||||
$primary_menu = this.$element.find('a[data-menu=' + menu_id + ']');
|
||||
}
|
||||
if (!$primary_menu.length) {
|
||||
return;
|
||||
}
|
||||
$primary_menu.addClass('active');
|
||||
this.$secondary_menu.find(
|
||||
'div[data-menu-parent=' + $primary_menu.data('menu') + ']').addClass('active').toggle(!this.folded);
|
||||
},
|
||||
on_menu_click: function(ev, id) {
|
||||
id = id || 0;
|
||||
var $clicked_menu, manual = false;
|
||||
|
||||
if (id) {
|
||||
// We can manually activate a menu with it's id (for hash url mapping)
|
||||
manual = true;
|
||||
$clicked_menu = this.$element.find('a[data-menu=' + id + ']');
|
||||
if (!$clicked_menu.length) {
|
||||
$clicked_menu = this.$secondary_menu.find('a[data-menu=' + id + ']');
|
||||
}
|
||||
} else {
|
||||
$clicked_menu = $(ev.currentTarget);
|
||||
id = $clicked_menu.data('menu');
|
||||
}
|
||||
|
||||
if (this.do_menu_click($clicked_menu, manual) && id) {
|
||||
this.current_menu = id;
|
||||
this.session.active_id = id;
|
||||
this.rpc('/web/menu/action', {'menu_id': id}, this.on_menu_action_loaded);
|
||||
}
|
||||
if (ev) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
do_menu_click: function($clicked_menu, manual) {
|
||||
var $sub_menu, $main_menu,
|
||||
active = $clicked_menu.is('.active'),
|
||||
sub_menu_visible = false,
|
||||
has_submenu_items = false;
|
||||
|
||||
if (this.$secondary_menu.has($clicked_menu).length) {
|
||||
$sub_menu = $clicked_menu.parents('.oe_secondary_menu');
|
||||
$main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
|
||||
} else {
|
||||
$sub_menu = this.$secondary_menu.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
|
||||
$main_menu = $clicked_menu;
|
||||
}
|
||||
|
||||
sub_menu_visible = $sub_menu.is(':visible');
|
||||
has_submenu_items = !!$sub_menu.children().length;
|
||||
this.$secondary_menu.find('.oe_secondary_menu').hide();
|
||||
|
||||
$('.active', this.$element.add(this.$secondary_menu)).removeClass('active');
|
||||
$main_menu.add($clicked_menu).add($sub_menu).addClass('active');
|
||||
|
||||
if (has_submenu_items) {
|
||||
if (!(this.folded && manual)) {
|
||||
this.do_show_secondary($sub_menu, $main_menu);
|
||||
} else {
|
||||
this.do_show_secondary();
|
||||
}
|
||||
}
|
||||
|
||||
if ($main_menu != $clicked_menu) {
|
||||
if ($clicked_menu.is('.submenu')) {
|
||||
$sub_menu.find('.submenu.opened').each(function() {
|
||||
if (!$(this).next().has($clicked_menu).length && !$(this).is($clicked_menu)) {
|
||||
$(this).removeClass('opened').next().hide();
|
||||
}
|
||||
});
|
||||
$clicked_menu.toggleClass('opened').next().toggle();
|
||||
} else if ($clicked_menu.is('.leaf')) {
|
||||
$sub_menu.toggle(!this.folded);
|
||||
return true;
|
||||
}
|
||||
} else if (this.folded) {
|
||||
if ((active && sub_menu_visible) || !has_submenu_items) {
|
||||
$sub_menu.hide();
|
||||
return true;
|
||||
}
|
||||
return manual;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
do_hide_secondary: function() {
|
||||
this.$secondary_menu.hide();
|
||||
},
|
||||
do_show_secondary: function($sub_menu, $main_menu) {
|
||||
var self = this;
|
||||
this.$secondary_menu.show();
|
||||
if (!arguments.length) {
|
||||
return;
|
||||
}
|
||||
if (this.folded) {
|
||||
var css = $main_menu.position(),
|
||||
fold_width = this.$secondary_menu.width() + 2,
|
||||
window_width = $(window).width();
|
||||
css.top += 33;
|
||||
css.left -= Math.round(($sub_menu.width() - $main_menu.width()) / 2);
|
||||
css.left = css.left < fold_width ? fold_width : css.left;
|
||||
if ((css.left + $sub_menu.width()) > window_width) {
|
||||
delete(css.left);
|
||||
css.right = 1;
|
||||
}
|
||||
$sub_menu.css(css);
|
||||
$sub_menu.mouseenter(function() {
|
||||
clearTimeout($sub_menu.data('timeoutId'));
|
||||
$sub_menu.data('timeoutId', null);
|
||||
return false;
|
||||
}).mouseleave(function(evt) {
|
||||
var timeoutId = setTimeout(function() {
|
||||
if (self.folded && $sub_menu.data('timeoutId')) {
|
||||
$sub_menu.hide().unbind('mouseenter').unbind('mouseleave');
|
||||
}
|
||||
}, self.float_timeout);
|
||||
$sub_menu.data('timeoutId', timeoutId);
|
||||
return false;
|
||||
self.rpc("/web/webclient/version_info", {}).then(function(res) {
|
||||
var $help = $(QWeb.render("About-Page", {version_info: res}));
|
||||
$help.find('a.oe_activate_debug_mode').click(function (e) {
|
||||
e.preventDefault();
|
||||
window.location = $.param.querystring(
|
||||
window.location.href, 'debug');
|
||||
});
|
||||
}
|
||||
$sub_menu.show();
|
||||
openerp.web.dialog($help, {autoOpen: true,
|
||||
modal: true, width: 960, title: _t("About")});
|
||||
});
|
||||
},
|
||||
on_menu_action_loaded: function(data) {
|
||||
var self = this;
|
||||
if (data.action.length) {
|
||||
var action = data.action[0][2];
|
||||
action.from_menu = true;
|
||||
self.on_action(action);
|
||||
} else {
|
||||
self.on_action({type: 'null_action'});
|
||||
}
|
||||
},
|
||||
on_action: function(action) {
|
||||
on_menu_shortcut: function($link) {
|
||||
var self = this,
|
||||
id = $link.data('id');
|
||||
self.session.active_id = id;
|
||||
self.rpc('/web/menu/action', {'menu_id': id}, function(ir_menu_data) {
|
||||
if (ir_menu_data.action.length){
|
||||
self.on_action(ir_menu_data.action[0][2]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebClient */{
|
||||
openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{
|
||||
/**
|
||||
* @constructs openerp.web.WebClient
|
||||
* @extends openerp.web.OldWidget
|
||||
*
|
||||
* @param element_id
|
||||
* @extends openerp.web.Widget
|
||||
*/
|
||||
init: function(parent) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
openerp.webclient = this;
|
||||
|
||||
this.querystring = '?' + jQuery.param.querystring();
|
||||
this._current_state = null;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.$element = $(document.body);
|
||||
this.$element.addClass("openerp openerp2");
|
||||
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
|
||||
this.$element.addClass("kitten-mode-activated");
|
||||
this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
|
||||
self.$element.toggleClass('clark-gable');
|
||||
});
|
||||
}
|
||||
this.session.bind().then(function() {
|
||||
this.session.bind_session().then(function() {
|
||||
if (!self.session.session_is_valid()) {
|
||||
self.show_login();
|
||||
}
|
||||
});
|
||||
this.session.on_session_valid.add(function() {
|
||||
self.show_application();
|
||||
|
||||
self.header.do_update();
|
||||
|
||||
self.user_menu.do_update();
|
||||
self.menu.do_reload();
|
||||
if(self.action_manager)
|
||||
self.action_manager.stop();
|
||||
self.action_manager.destroy();
|
||||
self.action_manager = new openerp.web.ActionManager(self);
|
||||
self.action_manager.appendTo($("#oe_app"));
|
||||
self.action_manager.appendTo(self.$element.find('.oe_application'));
|
||||
self.bind_hashchange();
|
||||
var version_label = _t("OpenERP - Unsupported/Community Version");
|
||||
if (!self.session.openerp_entreprise) {
|
||||
|
@ -1103,6 +978,9 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
|
|||
document.title = version_label;
|
||||
}
|
||||
});
|
||||
this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
|
||||
$(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
|
||||
});
|
||||
},
|
||||
show_login: function() {
|
||||
var self = this;
|
||||
|
@ -1115,15 +993,15 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
|
|||
var self = this;
|
||||
this.destroy_content();
|
||||
this.show_common();
|
||||
self.$table = $(QWeb.render("Interface", {}));
|
||||
self.$table = $(QWeb.render("WebClient", {}));
|
||||
self.$element.append(self.$table);
|
||||
self.header = new openerp.web.Header(self);
|
||||
self.header.on_logout.add(this.proxy('on_logout'));
|
||||
self.header.on_action.add(this.proxy('on_menu_action'));
|
||||
self.header.appendTo($("#oe_header"));
|
||||
self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu");
|
||||
self.menu = new openerp.web.Menu(self);
|
||||
self.menu.replace(this.$element.find('.oe_menu_placeholder'));
|
||||
self.menu.on_action.add(this.proxy('on_menu_action'));
|
||||
self.menu.start();
|
||||
self.user_menu = new openerp.web.UserMenu(self);
|
||||
self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder'));
|
||||
self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
|
||||
self.user_menu.on_action.add(this.proxy('on_menu_action'));
|
||||
},
|
||||
show_common: function() {
|
||||
var self = this;
|
||||
|
@ -1144,8 +1022,8 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
|
|||
this.loading.appendTo(this.$element);
|
||||
},
|
||||
destroy_content: function() {
|
||||
_.each(_.clone(this.widget_children), function(el) {
|
||||
el.stop();
|
||||
_.each(_.clone(this.getChildren()), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
this.$element.children().remove();
|
||||
},
|
||||
|
@ -1207,6 +1085,12 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
|
|||
self.menu.on_menu_click(null, action.menu_id);
|
||||
});
|
||||
}
|
||||
},
|
||||
set_content_full_screen: function(fullscreen) {
|
||||
if (fullscreen)
|
||||
$(".oe_webclient", this.$element).addClass("oe_content_full_screen");
|
||||
else
|
||||
$(".oe_webclient", this.$element).removeClass("oe_content_full_screen");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1250,7 +1134,7 @@ openerp.web.embed = function (origin, dbname, login, key, action, options) {
|
|||
var sc = document.getElementsByTagName('script');
|
||||
currentScript = sc[sc.length-1];
|
||||
}
|
||||
openerp.connection.bind(origin).then(function () {
|
||||
openerp.connection.bind_session(origin).then(function () {
|
||||
openerp.connection.session_authenticate(dbname, login, key, true).then(function () {
|
||||
var client = new openerp.web.EmbeddedClient(action, options);
|
||||
client.insertAfter(currentScript);
|
||||
|
|
|
@ -10,110 +10,8 @@ if (!console.debug) {
|
|||
}
|
||||
|
||||
openerp.web.core = function(openerp) {
|
||||
/**
|
||||
* John Resig Class with factory improvement
|
||||
*/
|
||||
(function() {
|
||||
var initializing = false,
|
||||
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
||||
// The web Class implementation (does nothing)
|
||||
/**
|
||||
* Extended version of John Resig's Class pattern
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
openerp.web.Class = function(){};
|
||||
|
||||
/**
|
||||
* Subclass an existing class
|
||||
*
|
||||
* @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
|
||||
*/
|
||||
openerp.web.Class.extend = function(prop) {
|
||||
var _super = this.prototype;
|
||||
|
||||
// Instantiate a web class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] = typeof prop[name] == "function" &&
|
||||
typeof _super[name] == "function" &&
|
||||
fnTest.test(prop[name]) ?
|
||||
(function(name, fn) {
|
||||
return function() {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same
|
||||
// method but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so
|
||||
// we remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name]) :
|
||||
prop[name];
|
||||
}
|
||||
|
||||
// The dummy class constructor
|
||||
function Class() {
|
||||
// All construction is actually done in the init method
|
||||
if (!initializing && this.init) {
|
||||
var ret = this.init.apply(this, arguments);
|
||||
if (ret) { return ret; }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Class.include = function (properties) {
|
||||
for (var name in properties) {
|
||||
if (typeof properties[name] !== 'function'
|
||||
|| !fnTest.test(properties[name])) {
|
||||
prototype[name] = properties[name];
|
||||
} else if (typeof prototype[name] === 'function'
|
||||
&& prototype.hasOwnProperty(name)) {
|
||||
prototype[name] = (function (name, fn, previous) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
this._super = previous;
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
return ret;
|
||||
}
|
||||
})(name, properties[name], prototype[name]);
|
||||
} else if (typeof _super[name] === 'function') {
|
||||
prototype[name] = (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
this._super = _super[name];
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
return ret;
|
||||
}
|
||||
})(name, properties[name]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Populate our constructed prototype object
|
||||
Class.prototype = prototype;
|
||||
|
||||
// Enforce the constructor to be what we expect
|
||||
Class.constructor = Class;
|
||||
|
||||
// And make this class extendable
|
||||
Class.extend = arguments.callee;
|
||||
|
||||
return Class;
|
||||
};
|
||||
})();
|
||||
openerp.web.Class = nova.Class;
|
||||
|
||||
openerp.web.callback = function(obj, method) {
|
||||
var callback = function() {
|
||||
|
@ -174,31 +72,6 @@ openerp.web.callback = function(obj, method) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates an inherited class that replaces all the methods by null methods (methods
|
||||
* that does nothing and always return undefined).
|
||||
*
|
||||
* @param {Class} claz
|
||||
* @param {Object} add Additional functions to override.
|
||||
* @return {Class}
|
||||
*/
|
||||
openerp.web.generate_null_object_class = function(claz, add) {
|
||||
var newer = {};
|
||||
var copy_proto = function(prototype) {
|
||||
for (var name in prototype) {
|
||||
if(typeof prototype[name] == "function") {
|
||||
newer[name] = function() {};
|
||||
}
|
||||
}
|
||||
if (prototype.prototype)
|
||||
copy_proto(prototype.prototype);
|
||||
};
|
||||
copy_proto(claz.prototype);
|
||||
newer.init = openerp.web.Widget.prototype.init;
|
||||
var tmpclass = claz.extend(newer);
|
||||
return tmpclass.extend(add || {});
|
||||
};
|
||||
|
||||
/**
|
||||
* web error for lookup failure
|
||||
*
|
||||
|
@ -362,11 +235,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
|
|||
}
|
||||
});
|
||||
|
||||
openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.CallbackEnabled# */{
|
||||
/**
|
||||
* @constructs openerp.web.CallbackEnabled
|
||||
* @extends openerp.web.Class
|
||||
*/
|
||||
openerp.web.CallbackEnabledMixin = {
|
||||
init: function() {
|
||||
// Transform on_* method into openerp.web.callbacks
|
||||
for (var name in this) {
|
||||
|
@ -404,7 +273,14 @@ openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.Ca
|
|||
return self[method_name].apply(self, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
openerp.web.CallbackEnabled = openerp.web.Class.extend(_.extend({}, nova.GetterSetterMixin, openerp.web.CallbackEnabledMixin, {
|
||||
init: function() {
|
||||
nova.GetterSetterMixin.init.call(this);
|
||||
openerp.web.CallbackEnabledMixin.init.call(this);
|
||||
}
|
||||
}));
|
||||
|
||||
openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Connection# */{
|
||||
/**
|
||||
|
@ -422,7 +298,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
this.name = openerp._session_id;
|
||||
this.qweb_mutex = new $.Mutex();
|
||||
},
|
||||
bind: function(origin) {
|
||||
bind_session: function(origin) {
|
||||
var window_origin = location.protocol+"//"+location.host, self=this;
|
||||
this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
|
||||
this.prefix = this.origin;
|
||||
|
@ -445,6 +321,269 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
this.active_id = null;
|
||||
return this.session_init();
|
||||
},
|
||||
test_eval_get_context: function () {
|
||||
var asJS = function (arg) {
|
||||
if (arg instanceof py.object) {
|
||||
return arg.toJSON();
|
||||
}
|
||||
return arg;
|
||||
};
|
||||
|
||||
var datetime = new py.object();
|
||||
datetime.datetime = new py.type(function datetime() {
|
||||
throw new Error('datetime.datetime not implemented');
|
||||
});
|
||||
var date = datetime.date = new py.type(function date(y, m, d) {
|
||||
if (y instanceof Array) {
|
||||
d = y[2];
|
||||
m = y[1];
|
||||
y = y[0];
|
||||
}
|
||||
this.year = asJS(y);
|
||||
this.month = asJS(m);
|
||||
this.day = asJS(d);
|
||||
}, py.object, {
|
||||
strftime: function (args) {
|
||||
var f = asJS(args[0]), self = this;
|
||||
return new py.str(f.replace(/%([A-Za-z])/g, function (m, c) {
|
||||
switch (c) {
|
||||
case 'Y': return self.year;
|
||||
case 'm': return _.str.sprintf('%02d', self.month);
|
||||
case 'd': return _.str.sprintf('%02d', self.day);
|
||||
}
|
||||
throw new Error('ValueError: No known conversion for ' + m);
|
||||
}));
|
||||
}
|
||||
});
|
||||
date.__getattribute__ = function (name) {
|
||||
if (name === 'today') {
|
||||
return date.today;
|
||||
}
|
||||
throw new Error("AttributeError: object 'date' has no attribute '" + name +"'");
|
||||
};
|
||||
date.today = new py.def(function () {
|
||||
var d = new Date();
|
||||
return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
||||
});
|
||||
datetime.time = new py.type(function time() {
|
||||
throw new Error('datetime.time not implemented');
|
||||
});
|
||||
|
||||
var time = new py.object();
|
||||
time.strftime = new py.def(function (args) {
|
||||
return date.today.__call__().strftime(args);
|
||||
});
|
||||
|
||||
var relativedelta = new py.type(function relativedelta(args, kwargs) {
|
||||
if (!_.isEmpty(args)) {
|
||||
throw new Error('Extraction of relative deltas from existing datetimes not supported');
|
||||
}
|
||||
this.ops = kwargs;
|
||||
}, py.object, {
|
||||
__add__: function (other) {
|
||||
if (!(other instanceof datetime.date)) {
|
||||
return py.NotImplemented;
|
||||
}
|
||||
// TODO: test this whole mess
|
||||
var year = asJS(this.ops.year) || asJS(other.year);
|
||||
if (asJS(this.ops.years)) {
|
||||
year += asJS(this.ops.years);
|
||||
}
|
||||
|
||||
var month = asJS(this.ops.month) || asJS(other.month);
|
||||
if (asJS(this.ops.months)) {
|
||||
month += asJS(this.ops.months);
|
||||
// FIXME: no divmod in JS?
|
||||
while (month < 1) {
|
||||
year -= 1;
|
||||
month += 12;
|
||||
}
|
||||
while (month > 12) {
|
||||
year += 1;
|
||||
month -= 12;
|
||||
}
|
||||
}
|
||||
|
||||
var lastMonthDay = new Date(year, month, 0).getDate();
|
||||
var day = asJS(this.ops.day) || asJS(other.day);
|
||||
if (day > lastMonthDay) { day = lastMonthDay; }
|
||||
var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
|
||||
if (days_offset) {
|
||||
day = new Date(year, month-1, day + days_offset).getDate();
|
||||
}
|
||||
// TODO: leapdays?
|
||||
// TODO: hours, minutes, seconds? Not used in XML domains
|
||||
// TODO: weekday?
|
||||
return new datetime.date(year, month, day);
|
||||
},
|
||||
__radd__: function (other) {
|
||||
return this.__add__(other);
|
||||
},
|
||||
|
||||
__sub__: function (other) {
|
||||
if (!(other instanceof datetime.date)) {
|
||||
return py.NotImplemented;
|
||||
}
|
||||
// TODO: test this whole mess
|
||||
var year = asJS(this.ops.year) || asJS(other.year);
|
||||
if (asJS(this.ops.years)) {
|
||||
year -= asJS(this.ops.years);
|
||||
}
|
||||
|
||||
var month = asJS(this.ops.month) || asJS(other.month);
|
||||
if (asJS(this.ops.months)) {
|
||||
month -= asJS(this.ops.months);
|
||||
// FIXME: no divmod in JS?
|
||||
while (month < 1) {
|
||||
year -= 1;
|
||||
month += 12;
|
||||
}
|
||||
while (month > 12) {
|
||||
year += 1;
|
||||
month -= 12;
|
||||
}
|
||||
}
|
||||
|
||||
var lastMonthDay = new Date(year, month, 0).getDate();
|
||||
var day = asJS(this.ops.day) || asJS(other.day);
|
||||
if (day > lastMonthDay) { day = lastMonthDay; }
|
||||
var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
|
||||
if (days_offset) {
|
||||
day = new Date(year, month-1, day - days_offset).getDate();
|
||||
}
|
||||
// TODO: leapdays?
|
||||
// TODO: hours, minutes, seconds? Not used in XML domains
|
||||
// TODO: weekday?
|
||||
return new datetime.date(year, month, day);
|
||||
},
|
||||
__rsub__: function (other) {
|
||||
return this.__sub__(other);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
uid: new py.float(this.uid),
|
||||
datetime: datetime,
|
||||
time: time,
|
||||
relativedelta: relativedelta
|
||||
};
|
||||
},
|
||||
/**
|
||||
* FIXME: Huge testing hack, especially the evaluation context, rewrite + test for real before switching
|
||||
*/
|
||||
test_eval: function (source, expected) {
|
||||
try {
|
||||
var ctx = this.test_eval_contexts(source.contexts);
|
||||
if (!_.isEqual(ctx, expected.context)) {
|
||||
console.group('Local context does not match remote, nothing is broken but please report to R&D (xmo)');
|
||||
console.warn('source', source.contexts);
|
||||
console.warn('local', ctx);
|
||||
console.warn('remote', expected.context);
|
||||
console.groupEnd();
|
||||
}
|
||||
} catch (e) {
|
||||
console.group('Failed to evaluate contexts, nothing is broken but please report to R&D (xmo)');
|
||||
console.error(e);
|
||||
console.log('source', source.contexts);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
try {
|
||||
var dom = this.test_eval_domains(source.domains, this.test_eval_get_context());
|
||||
if (!_.isEqual(dom, expected.domain)) {
|
||||
console.group('Local domain does not match remote, nothing is broken but please report to R&D (xmo)');
|
||||
console.warn('source', source.domains);
|
||||
console.warn('local', dom);
|
||||
console.warn('remote', expected.domain);
|
||||
console.groupEnd();
|
||||
}
|
||||
} catch (e) {
|
||||
console.group('Failed to evaluate domains, nothing is broken but please report to R&D (xmo)');
|
||||
console.error(e);
|
||||
console.log('source', source.domains);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
try {
|
||||
var groups = this.test_eval_groupby(source.group_by_seq);
|
||||
if (!_.isEqual(groups, expected.group_by)) {
|
||||
console.group('Local groupby does not match remote, nothing is broken but please report to R&D (xmo)');
|
||||
console.warn('source', source.group_by_seq);
|
||||
console.warn('local', groups);
|
||||
console.warn('remote', expected.group_by);
|
||||
console.groupEnd();
|
||||
}
|
||||
} catch (e) {
|
||||
console.group('Failed to evaluate groupby, nothing is broken but please report to R&D (xmo)');
|
||||
console.error(e);
|
||||
console.log('source', source.group_by_seq);
|
||||
console.groupEnd();
|
||||
}
|
||||
},
|
||||
test_eval_contexts: function (contexts) {
|
||||
var result_context = _.extend({}, this.user_context),
|
||||
self = this;
|
||||
_(contexts).each(function (ctx) {
|
||||
switch(ctx.__ref) {
|
||||
case 'context':
|
||||
_.extend(result_context, py.eval(ctx.__debug));
|
||||
break;
|
||||
case 'compound_context':
|
||||
_.extend(
|
||||
result_context, self.test_eval_contexts(ctx.__contexts));
|
||||
break;
|
||||
default:
|
||||
_.extend(result_context, ctx);
|
||||
}
|
||||
});
|
||||
return result_context;
|
||||
},
|
||||
test_eval_domains: function (domains, eval_context) {
|
||||
var result_domain = [], self = this;
|
||||
_(domains).each(function (dom) {
|
||||
switch(dom.__ref) {
|
||||
case 'domain':
|
||||
result_domain.push.apply(
|
||||
result_domain, py.eval(dom.__debug, eval_context));
|
||||
break;
|
||||
case 'compound_domain':
|
||||
result_domain.push.apply(
|
||||
result_domain, self.test_eval_domains(
|
||||
dom.__domains, eval_context));
|
||||
break;
|
||||
default:
|
||||
result_domain.push.apply(
|
||||
result_domain, dom);
|
||||
}
|
||||
});
|
||||
return result_domain;
|
||||
},
|
||||
test_eval_groupby: function (contexts) {
|
||||
var result_group = [], self = this;
|
||||
_(contexts).each(function (ctx) {
|
||||
var group;
|
||||
switch(ctx.__ref) {
|
||||
case 'context':
|
||||
group = py.eval(ctx.__debug).group_by;
|
||||
break;
|
||||
case 'compound_context':
|
||||
group = self.test_eval_contexts(ctx.__contexts).group_by;
|
||||
break;
|
||||
default:
|
||||
group = ctx.group_by
|
||||
}
|
||||
if (!group) { return; }
|
||||
if (typeof group === 'string') {
|
||||
result_group.push(group);
|
||||
} else if (group instanceof Array) {
|
||||
result_group.push.apply(result_group, group);
|
||||
} else {
|
||||
throw new Error('Got invalid groupby {{'
|
||||
+ JSON.stringify(group) + '}}');
|
||||
}
|
||||
});
|
||||
return result_group;
|
||||
},
|
||||
/**
|
||||
* Executes an RPC call, registering the provided callbacks.
|
||||
*
|
||||
|
@ -480,6 +619,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
function (response, textStatus, jqXHR) {
|
||||
self.on_rpc_response();
|
||||
if (!response.error) {
|
||||
if (url.url === '/web/session/eval_domain_and_context') {
|
||||
self.test_eval(params, response.result);
|
||||
}
|
||||
deferred.resolve(response["result"], textStatus, jqXHR);
|
||||
} else if (response.error.data.type === "session_invalid") {
|
||||
self.uid = false;
|
||||
|
@ -733,6 +875,14 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
|
||||
return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
|
||||
return self.do_load_js(file_list.concat(files));
|
||||
}).then(function () {
|
||||
if (!Date.CultureInfo.pmDesignator) {
|
||||
// If no am/pm designator is specified but the openerp
|
||||
// datetime format uses %i, date.js won't be able to
|
||||
// correctly format a date. See bug#938497.
|
||||
Date.CultureInfo.amDesignator = 'AM';
|
||||
Date.CultureInfo.pmDesignator = 'PM';
|
||||
}
|
||||
});
|
||||
}))
|
||||
}
|
||||
|
@ -873,9 +1023,12 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
_(_.extend({}, options.data || {},
|
||||
{session_id: this.session_id, token: token}))
|
||||
.each(function (value, key) {
|
||||
$('<input type="hidden" name="' + key + '">')
|
||||
.val(value)
|
||||
.appendTo($form_data);
|
||||
var $input = $form.find('[name=' + key +']');
|
||||
if (!$input.length) {
|
||||
$input = $('<input type="hidden" name="' + key + '">')
|
||||
.appendTo($form_data);
|
||||
}
|
||||
$input.val(value)
|
||||
});
|
||||
|
||||
$form
|
||||
|
@ -958,11 +1111,11 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
|||
*
|
||||
* And of course, when you don't need that widget anymore, just do:
|
||||
*
|
||||
* my_widget.stop();
|
||||
* my_widget.destroy();
|
||||
*
|
||||
* That will kill the widget in a clean way and erase its content from the dom.
|
||||
*/
|
||||
openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.Widget# */{
|
||||
openerp.web.Widget = nova.Widget.extend(_.extend({}, openerp.web.CallbackEnabledMixin, {
|
||||
/**
|
||||
* The name of the QWeb template that will be used for rendering. Must be
|
||||
* redefined in subclasses or the default render() method can not be used.
|
||||
|
@ -970,11 +1123,6 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
|
|||
* @type string
|
||||
*/
|
||||
template: null,
|
||||
/**
|
||||
* Tag name when creating a default $element.
|
||||
* @type string
|
||||
*/
|
||||
tag_name: 'div',
|
||||
/**
|
||||
* Constructs the widget and sets its parent if a parent is given.
|
||||
*
|
||||
|
@ -982,7 +1130,7 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
|
|||
* @extends openerp.web.CallbackEnabled
|
||||
*
|
||||
* @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
|
||||
* When that widget is destroyed by calling stop(), the current instance will be
|
||||
* When that widget is destroyed by calling destroy(), the current instance will be
|
||||
* destroyed too. Can be null.
|
||||
* @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
|
||||
* to bind the current Widget to an already existing part of the DOM, which is not compatible
|
||||
|
@ -990,132 +1138,24 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
|
|||
* for new components this argument should not be provided any more.
|
||||
*/
|
||||
init: function(parent) {
|
||||
this._super();
|
||||
this._super(parent);
|
||||
openerp.web.CallbackEnabledMixin.init.call(this);
|
||||
this.session = openerp.connection;
|
||||
|
||||
this.$element = $(document.createElement(this.tag_name));
|
||||
|
||||
this.widget_parent = parent;
|
||||
this.widget_children = [];
|
||||
if(parent && parent.widget_children) {
|
||||
parent.widget_children.push(this);
|
||||
}
|
||||
// useful to know if the widget was destroyed and should not be used anymore
|
||||
this.widget_is_stopped = false;
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and appends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
* Renders the element. The default implementation renders the widget using QWeb,
|
||||
* `this.template` must be defined. The context given to QWeb contains the "widget"
|
||||
* key that references `this`.
|
||||
*/
|
||||
appendTo: function(target) {
|
||||
var self = this;
|
||||
return this._render_and_insert(function(t) {
|
||||
self.$element.appendTo(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and prepends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
prependTo: function(target) {
|
||||
var self = this;
|
||||
return this._render_and_insert(function(t) {
|
||||
self.$element.prependTo(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it after to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertAfter: function(target) {
|
||||
var self = this;
|
||||
return this._render_and_insert(function(t) {
|
||||
self.$element.insertAfter(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it before to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertBefore: function(target) {
|
||||
var self = this;
|
||||
return this._render_and_insert(function(t) {
|
||||
self.$element.insertBefore(t);
|
||||
}, target);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and replaces the given jQuery object.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
replace: function(target) {
|
||||
return this._render_and_insert(_.bind(function(t) {
|
||||
this.$element.replaceAll(t);
|
||||
}, this), target);
|
||||
},
|
||||
_render_and_insert: function(insertion, target) {
|
||||
this.render_element();
|
||||
if (target instanceof openerp.web.Widget)
|
||||
target = target.$element;
|
||||
insertion(target);
|
||||
this.on_inserted(this.$element, this);
|
||||
return this.start();
|
||||
},
|
||||
on_inserted: function(element, widget) {},
|
||||
/**
|
||||
* Renders the element and insert the result of the render() method in this.$element.
|
||||
*/
|
||||
render_element: function() {
|
||||
var rendered = this.render();
|
||||
if (rendered) {
|
||||
renderElement: function() {
|
||||
var rendered = null;
|
||||
if (this.template)
|
||||
rendered = openerp.web.qweb.render(this.template, {widget: this});
|
||||
if (_.str.trim(rendered)) {
|
||||
var elem = $(rendered);
|
||||
this.$element.replaceWith(elem);
|
||||
this.$element = elem;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Renders the widget using QWeb, `this.template` must be defined.
|
||||
* The context given to QWeb contains the "widget" key that references `this`.
|
||||
*
|
||||
* @param {Object} additional Additional context arguments to pass to the template.
|
||||
*/
|
||||
render: function (additional) {
|
||||
if (this.template)
|
||||
return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Method called after rendering. Mostly used to bind actions, perform asynchronous
|
||||
* calls, etc...
|
||||
*
|
||||
* By convention, the method should return a promise to inform the caller when
|
||||
* this widget has been initialized.
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
start: function() {
|
||||
return $.Deferred().done().promise();
|
||||
},
|
||||
/**
|
||||
* Destroys the current widget, also destroys all its children before destroying itself.
|
||||
*/
|
||||
stop: function() {
|
||||
_.each(_.clone(this.widget_children), function(el) {
|
||||
el.stop();
|
||||
});
|
||||
if(this.$element != null) {
|
||||
this.$element.remove();
|
||||
}
|
||||
if (this.widget_parent && this.widget_parent.widget_children) {
|
||||
this.widget_parent.widget_children = _.without(this.widget_parent.widget_children, this);
|
||||
}
|
||||
this.widget_parent = null;
|
||||
this.widget_is_stopped = true;
|
||||
},
|
||||
/**
|
||||
* Informs the action manager to do an action. This supposes that
|
||||
|
@ -1123,37 +1163,36 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
|
|||
* If that's not the case this method will simply return `false`.
|
||||
*/
|
||||
do_action: function(action, on_finished) {
|
||||
if (this.widget_parent) {
|
||||
return this.widget_parent.do_action(action, on_finished);
|
||||
if (this.getParent()) {
|
||||
return this.getParent().do_action(action, on_finished);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
do_notify: function() {
|
||||
if (this.widget_parent) {
|
||||
return this.widget_parent.do_notify.apply(this,arguments);
|
||||
if (this.getParent()) {
|
||||
return this.getParent().do_notify.apply(this,arguments);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
do_warn: function() {
|
||||
if (this.widget_parent) {
|
||||
return this.widget_parent.do_warn.apply(this,arguments);
|
||||
if (this.getParent()) {
|
||||
return this.getParent().do_warn.apply(this,arguments);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
rpc: function(url, data, success, error) {
|
||||
var def = $.Deferred().then(success, error);
|
||||
var self = this;
|
||||
openerp.connection.rpc(url, data). then(function() {
|
||||
if (!self.widget_is_stopped)
|
||||
if (!self.isDestroyed())
|
||||
def.resolve.apply(def, arguments);
|
||||
}, function() {
|
||||
if (!self.widget_is_stopped)
|
||||
if (!self.isDestroyed())
|
||||
def.reject.apply(def, arguments);
|
||||
});
|
||||
return def.promise();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
/**
|
||||
* @deprecated use :class:`openerp.web.Widget`
|
||||
|
@ -1164,7 +1203,21 @@ openerp.web.OldWidget = openerp.web.Widget.extend({
|
|||
this.element_id = element_id;
|
||||
this.element_id = this.element_id || _.uniqueId('widget-');
|
||||
var tmp = document.getElementById(this.element_id);
|
||||
this.$element = tmp ? $(tmp) : $(document.createElement(this.tag_name));
|
||||
this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
|
||||
},
|
||||
renderElement: function() {
|
||||
var rendered = this.render();
|
||||
if (rendered) {
|
||||
var elem = $(rendered);
|
||||
this.$element.replaceWith(elem);
|
||||
this.$element = elem;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
render: function (additional) {
|
||||
if (this.template)
|
||||
return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1196,7 +1249,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
|
|||
if (translation_bundle.lang_parameters) {
|
||||
this.parameters = translation_bundle.lang_parameters;
|
||||
this.parameters.grouping = py.eval(
|
||||
this.parameters.grouping).toJSON();
|
||||
this.parameters.grouping);
|
||||
}
|
||||
},
|
||||
add_module_translation: function(mod) {
|
||||
|
@ -1244,7 +1297,7 @@ openerp.web._lt = function (s) {
|
|||
return {toString: function () { return openerp.web._t(s); }}
|
||||
};
|
||||
openerp.web.qweb = new QWeb2.Engine();
|
||||
openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1);
|
||||
openerp.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
|
||||
openerp.web.qweb.default_dict = {
|
||||
'_' : _,
|
||||
'_t' : openerp.web._t
|
||||
|
@ -1279,6 +1332,23 @@ openerp.web.qweb.preprocess_node = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A small utility function to check if a class implements correctly an interface, assuming that
|
||||
* interface is simply specified using a dictionary containing methods and attributes with the
|
||||
* correct type. It only performs the check when in debug mode and the only effect of an invalid
|
||||
* check is messages in the console.
|
||||
*/
|
||||
openerp.web.check_interface = function(_class, _interface) {
|
||||
if (! openerp.web.check_interface.debug)
|
||||
return;
|
||||
for (var member in _interface) {
|
||||
if ( (typeof _class.prototype[member] != typeof _interface[member]) ) {
|
||||
console.error("class failed to implement interface member '" + member + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
openerp.web.check_interface.debug = ($.deparam($.param.querystring()).debug != undefined);
|
||||
|
||||
/** Jquery extentions */
|
||||
$.Mutex = (function() {
|
||||
function Mutex() {
|
||||
|
|
|
@ -1012,18 +1012,25 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
|||
: (v1 > v2) ? 1
|
||||
: 0;
|
||||
};
|
||||
records.sort(function (a, b) {
|
||||
return _.reduce(sort_fields, function (acc, field) {
|
||||
if (acc) { return acc; }
|
||||
|
||||
var sign = 1;
|
||||
if (field[0] === '-') {
|
||||
sign = -1;
|
||||
field = field.slice(1);
|
||||
}
|
||||
return sign * compare(a[field], b[field]);
|
||||
}, 0);
|
||||
});
|
||||
// Array.sort is not necessarily stable. We must be careful with this because
|
||||
// sorting an array where all items are considered equal is a worst-case that
|
||||
// will randomize the array with an unstable sort! Therefore we must avoid
|
||||
// sorting if there are no sort_fields (i.e. all items are considered equal)
|
||||
// See also: http://ecma262-5.com/ELS5_Section_15.htm#Section_15.4.4.11
|
||||
// http://code.google.com/p/v8/issues/detail?id=90
|
||||
if (sort_fields.length) {
|
||||
records.sort(function (a, b) {
|
||||
return _.reduce(sort_fields, function (acc, field) {
|
||||
if (acc) { return acc; }
|
||||
var sign = 1;
|
||||
if (field[0] === '-') {
|
||||
sign = -1;
|
||||
field = field.slice(1);
|
||||
}
|
||||
return sign * compare(a[field], b[field]);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
completion.resolve(records);
|
||||
};
|
||||
if(to_get.length > 0) {
|
||||
|
|
|
@ -66,11 +66,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
|||
this._super();
|
||||
this.open({
|
||||
buttons: [
|
||||
{text: _t("Close"), click: function() { self.stop(); }},
|
||||
{text: _t("Close"), click: function() { self.destroy(); }},
|
||||
{text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
|
||||
],
|
||||
close: function(event, ui) {
|
||||
self.stop();
|
||||
self.destroy();
|
||||
}
|
||||
});
|
||||
this.toggle_import_button(false);
|
||||
|
@ -151,7 +151,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
|||
});
|
||||
},
|
||||
toggle_import_button: function (newstate) {
|
||||
this.$element.dialog('widget')
|
||||
openerp.web.dialog(this.$element, 'widget')
|
||||
.find('.oe-dialog-import-button')
|
||||
.button('option', 'disabled', !newstate);
|
||||
},
|
||||
|
@ -201,10 +201,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
|||
return;
|
||||
}
|
||||
if (results['success']) {
|
||||
if (this.widget_parent.widget_parent.active_view == "list") {
|
||||
this.widget_parent.reload_content();
|
||||
if (this.getParent().getParent().active_view == "list") {
|
||||
this.getParent().reload_content();
|
||||
}
|
||||
this.stop();
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
|||
}
|
||||
return true;
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
this.$element.remove();
|
||||
this._super();
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
|
|||
if (typeof(value) == "string")
|
||||
value = openerp.web.auto_str_to_date(value);
|
||||
return value.toString(normalize_format(l10n.time_format));
|
||||
case 'selection':
|
||||
case 'selection': case 'statusbar':
|
||||
// Each choice is [value, label]
|
||||
if(_.isArray(value)) {
|
||||
value = value[0]
|
||||
|
@ -336,7 +336,7 @@ openerp.web.format_cell = function (row_data, column, options) {
|
|||
case 'progressbar':
|
||||
return _.template(
|
||||
'<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
|
||||
value: row_data[column.id].value
|
||||
value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
this.hidden = !!hidden;
|
||||
this.headless = this.hidden && !this.has_defaults;
|
||||
|
||||
this.filter_data = {};
|
||||
|
||||
this.ready = $.Deferred();
|
||||
},
|
||||
start: function() {
|
||||
|
@ -232,7 +234,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
context.add({"group_by": groupbys});
|
||||
var dial_html = QWeb.render("SearchView.managed-filters.add");
|
||||
var $dial = $(dial_html);
|
||||
$dial.dialog({
|
||||
openerp.web.dialog($dial, {
|
||||
modal: true,
|
||||
title: _t("Filter Entry"),
|
||||
buttons: [
|
||||
|
@ -263,10 +265,20 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
var filter = this.managed_filters[val];
|
||||
this.do_clear(false).then(_.bind(function() {
|
||||
select.val('get:' + val);
|
||||
var groupbys = _.map(filter.context.group_by.split(","), function(el) {
|
||||
return {"group_by": el};
|
||||
});
|
||||
this.on_search([filter.domain], [filter.context], groupbys);
|
||||
|
||||
var groupbys = [];
|
||||
var group_by = filter.context.group_by;
|
||||
if (group_by) {
|
||||
groupbys = _.map(
|
||||
group_by instanceof Array ? group_by : group_by.split(','),
|
||||
function (el) { return { group_by: el }; });
|
||||
}
|
||||
this.filter_data = {
|
||||
domains: [filter.domain],
|
||||
contexts: [filter.context],
|
||||
groupbys: groupbys
|
||||
};
|
||||
this.do_search();
|
||||
}, this));
|
||||
} else {
|
||||
select.val('');
|
||||
|
@ -281,7 +293,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
selected_menu_id : menu.$element.find('a.active').data('menu')
|
||||
}));
|
||||
$dialog.find('input').val(this.fields_view.name);
|
||||
$dialog.dialog({
|
||||
openerp.web.dialog($dialog, {
|
||||
modal: true,
|
||||
title: _t("Add to Dashboard"),
|
||||
buttons: [
|
||||
|
@ -303,10 +315,10 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
});
|
||||
self.rpc('/web/searchview/add_to_dashboard', {
|
||||
menu_id: menu_id,
|
||||
action_id: self.widget_parent.action.id,
|
||||
action_id: self.getParent().action.id,
|
||||
context_to_save: context,
|
||||
domain: domain,
|
||||
view_mode: self.widget_parent.active_view,
|
||||
view_mode: self.getParent().active_view,
|
||||
name: title
|
||||
}, function(r) {
|
||||
if (r === false) {
|
||||
|
@ -334,9 +346,6 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
if (this.headless && !this.has_defaults) {
|
||||
return this.on_search([], [], []);
|
||||
}
|
||||
// reset filters management
|
||||
var select = this.$element.find(".oe_search-view-filters-management");
|
||||
select.val("_filters");
|
||||
|
||||
if (e && e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
|
@ -380,6 +389,16 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
.map(function (filter) { return filter.get_context();})
|
||||
.compact()
|
||||
.value();
|
||||
|
||||
if (this.filter_data.contexts) {
|
||||
contexts = this.filter_data.contexts.concat(contexts)
|
||||
}
|
||||
if (this.filter_data.domains) {
|
||||
domains = this.filter_data.domains.concat(domains);
|
||||
}
|
||||
if (this.filter_data.groupbys) {
|
||||
groupbys = this.filter_data.groupbys.concat(groupbys);
|
||||
}
|
||||
return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
|
||||
},
|
||||
/**
|
||||
|
@ -418,6 +437,9 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
|
|||
* @param {Boolean} [reload_view=true]
|
||||
*/
|
||||
do_clear: function (reload_view) {
|
||||
this.filter_data = {};
|
||||
this.$element.find(".oe_search-view-filters-management").val('');
|
||||
|
||||
this.$element.find('.filter_label, .filter_icon').removeClass('enabled');
|
||||
this.enabled_filters.splice(0);
|
||||
var string = $('a.searchview_group_string');
|
||||
|
@ -552,7 +574,7 @@ openerp.web.search.Widget = openerp.web.OldWidget.extend( /** @lends openerp.web
|
|||
* "Stops" the widgets. Called when the view destroys itself, this
|
||||
* lets the widgets clean up after themselves.
|
||||
*/
|
||||
stop: function () {
|
||||
destroy: function () {
|
||||
delete this.view;
|
||||
this._super();
|
||||
},
|
||||
|
@ -1114,7 +1136,7 @@ openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
|
|||
if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
|
||||
return null;
|
||||
}
|
||||
return _.reduce(this.widget_children,
|
||||
return _.reduce(this.getChildren(),
|
||||
function(mem, x) { return mem.concat(x.get_domain());}, []);
|
||||
},
|
||||
on_activate: function() {
|
||||
|
@ -1133,9 +1155,9 @@ openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
|
|||
}
|
||||
},
|
||||
check_last_element: function() {
|
||||
_.each(this.widget_children, function(x) {x.set_last_group(false);});
|
||||
if (this.widget_children.length >= 1) {
|
||||
this.widget_children[this.widget_children.length - 1].set_last_group(true);
|
||||
_.each(this.getChildren(), function(x) {x.set_last_group(false);});
|
||||
if (this.getChildren().length >= 1) {
|
||||
this.getChildren()[this.getChildren().length - 1].set_last_group(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1148,7 +1170,7 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
|
|||
},
|
||||
add_prop: function() {
|
||||
var prop = new openerp.web.search.ExtendedSearchProposition(this, this.fields);
|
||||
var render = prop.render({'index': this.widget_children.length - 1});
|
||||
var render = prop.render({'index': this.getChildren().length - 1});
|
||||
this.$element.find('.searchview_extended_propositions_list').append(render);
|
||||
prop.start();
|
||||
},
|
||||
|
@ -1159,11 +1181,11 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
|
|||
_this.add_prop();
|
||||
});
|
||||
this.$element.find('.searchview_extended_delete_group').click(function () {
|
||||
_this.stop();
|
||||
_this.destroy();
|
||||
});
|
||||
},
|
||||
get_domain: function() {
|
||||
var props = _(this.widget_children).chain().map(function(x) {
|
||||
var props = _(this.getChildren()).chain().map(function(x) {
|
||||
return x.get_proposition();
|
||||
}).compact().value();
|
||||
var choice = this.$element.find(".searchview_extended_group_choice").val();
|
||||
|
@ -1172,10 +1194,10 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
|
|||
_.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
|
||||
props);
|
||||
},
|
||||
stop: function() {
|
||||
var parent = this.widget_parent;
|
||||
if (this.widget_parent.widget_children.length == 1)
|
||||
this.widget_parent.hide();
|
||||
destroy: function() {
|
||||
var parent = this.getParent();
|
||||
if (this.getParent().getChildren().length == 1)
|
||||
this.getParent().hide();
|
||||
this._super();
|
||||
parent.check_last_element();
|
||||
},
|
||||
|
@ -1210,16 +1232,16 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
|
|||
_this.changed();
|
||||
});
|
||||
this.$element.find('.searchview_extended_delete_prop').click(function () {
|
||||
_this.stop();
|
||||
_this.destroy();
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
var parent;
|
||||
if (this.widget_parent.widget_children.length == 1)
|
||||
parent = this.widget_parent;
|
||||
if (this.getParent().getChildren().length == 1)
|
||||
parent = this.getParent();
|
||||
this._super();
|
||||
if (parent)
|
||||
parent.stop();
|
||||
parent.destroy();
|
||||
},
|
||||
changed: function() {
|
||||
var nval = this.$element.find(".searchview_extended_prop_field").val();
|
||||
|
@ -1235,7 +1257,7 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
|
|||
select_field: function(field) {
|
||||
var self = this;
|
||||
if(this.attrs.selected != null) {
|
||||
this.value.stop();
|
||||
this.value.destroy();
|
||||
this.value = null;
|
||||
this.$element.find('.searchview_extended_prop_op').html('');
|
||||
}
|
||||
|
|
|
@ -1002,10 +1002,10 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
|
|||
$.when(action_manager.do_action(action)).then(function() {
|
||||
var controller = action_manager.dialog_viewmanager.views['form'].controller;
|
||||
controller.on_button_cancel.add_last(function(){
|
||||
action_manager.stop()
|
||||
action_manager.destroy()
|
||||
});
|
||||
controller.do_save.add_last(function(){
|
||||
action_manager.stop();
|
||||
action_manager.destroy();
|
||||
var value =controller.fields.name.value;
|
||||
self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value",value).text(value));
|
||||
_.detect(self.add_widget,function(widget){
|
||||
|
|
|
@ -74,13 +74,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
}, this.on_loaded);
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
if (this.sidebar) {
|
||||
this.sidebar.attachments.stop();
|
||||
this.sidebar.stop();
|
||||
this.sidebar.attachments.destroy();
|
||||
this.sidebar.destroy();
|
||||
}
|
||||
_.each(this.widgets, function(w) {
|
||||
w.stop();
|
||||
w.destroy();
|
||||
});
|
||||
this._super();
|
||||
},
|
||||
|
@ -90,17 +90,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
},
|
||||
on_loaded: function(data) {
|
||||
var self = this;
|
||||
if (data) {
|
||||
this.fields_order = [];
|
||||
this.fields_view = data;
|
||||
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
||||
|
||||
this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this });
|
||||
if (!data) {
|
||||
throw "No data provided.";
|
||||
}
|
||||
if (this.root_frame) {
|
||||
throw "Form view does not support multiple calls to on_loaded";
|
||||
}
|
||||
this.fields_order = [];
|
||||
this.fields_view = data;
|
||||
|
||||
this.rendered = QWeb.render(this.form_template, {'widget': this});
|
||||
this.$element.html(this.rendered);
|
||||
_.each(this.widgets, function(w) {
|
||||
w.start();
|
||||
});
|
||||
|
||||
this.root_frame = instanciate_widget(this.registry.get_object('frame'), this, this.fields_view.arch);
|
||||
var to_append = $(".oe_form_header", this.$element);
|
||||
this.root_frame.appendTo(to_append.length > 0 ? to_append : this.$element);
|
||||
this.root_frame.$element.children().unwrap();
|
||||
|
||||
this.$form_header = this.$element.find('.oe_form_header:first');
|
||||
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
||||
var action = $(this).data('pager-action');
|
||||
|
@ -205,7 +211,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
if (self.sidebar) {
|
||||
self.sidebar.attachments.do_update();
|
||||
}
|
||||
if (self.default_focus_field && !self.embedded_view) {
|
||||
if (self.default_focus_field) {
|
||||
self.default_focus_field.focus();
|
||||
}
|
||||
if (record.id) {
|
||||
|
@ -418,7 +424,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
this.on_form_changed();
|
||||
}
|
||||
if (!_.isEmpty(result.warning)) {
|
||||
$(QWeb.render("CrashManagerWarning", result.warning)).dialog({
|
||||
openerp.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), {
|
||||
modal: true,
|
||||
buttons: [
|
||||
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
|
||||
|
@ -879,7 +885,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
|
|||
};
|
||||
|
||||
openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
|
||||
template: 'Widget',
|
||||
form_template: 'Widget',
|
||||
/**
|
||||
* @constructs openerp.web.form.Widget
|
||||
* @extends openerp.web.OldWidget
|
||||
|
@ -894,13 +900,9 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
|||
this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
|
||||
this.type = this.type || node.tag;
|
||||
this.element_name = this.element_name || this.type;
|
||||
this.element_class = [
|
||||
'formview', this.view.view_id, this.element_name,
|
||||
this.view.widgets_counter++].join("_");
|
||||
|
||||
this._super(view);
|
||||
|
||||
this.view.widgets[this.element_class] = this;
|
||||
this.children = node.children;
|
||||
this.colspan = parseInt(node.attrs.colspan || 1, 10);
|
||||
this.decrease_max_width = 0;
|
||||
|
@ -919,14 +921,9 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
|||
this.align = 'center';
|
||||
}
|
||||
|
||||
|
||||
this.width = this.node.attrs.width;
|
||||
},
|
||||
start: function() {
|
||||
this.$element = this.view.$element.find(
|
||||
'.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
this._super.apply(this, arguments);
|
||||
$.fn.tipsy.clear();
|
||||
},
|
||||
|
@ -939,9 +936,8 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
|||
update_dom: function() {
|
||||
this.$element.toggle(!this.invisible);
|
||||
},
|
||||
render: function() {
|
||||
var template = this.template;
|
||||
return QWeb.render(template, { "widget": this });
|
||||
renderElement: function() {
|
||||
this.$element.html(QWeb.render(this.form_template, { "widget": this }));
|
||||
},
|
||||
do_attach_tooltip: function(widget, trigger, options) {
|
||||
widget = widget || this;
|
||||
|
@ -951,7 +947,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
|||
delayOut: 0,
|
||||
fade: true,
|
||||
title: function() {
|
||||
var template = widget.template + '.tooltip';
|
||||
var template = widget.form_template + '.tooltip';
|
||||
if (!QWeb.has_template(template)) {
|
||||
template = 'WidgetLabel.tooltip';
|
||||
}
|
||||
|
@ -1016,7 +1012,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetFrame',
|
||||
form_template: 'WidgetFrame',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.columns = parseInt(node.attrs.col || 4, 10);
|
||||
|
@ -1034,6 +1030,23 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
|
|||
}
|
||||
this.set_row_cells_with(this.table[this.table.length - 1]);
|
||||
},
|
||||
renderElement: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
_.each(this.table, function(row) {
|
||||
_.each(row, function(td) {
|
||||
td.$element = self.$element.find('.' + td.element_class);
|
||||
td.renderElement();
|
||||
});
|
||||
});
|
||||
},
|
||||
start: function() {
|
||||
_.each(this.table, function(row) {
|
||||
_.each(row, function(td) {
|
||||
td.start();
|
||||
});
|
||||
});
|
||||
},
|
||||
add_row: function(){
|
||||
if (this.table.length) {
|
||||
this.set_row_cells_with(this.table[this.table.length - 1]);
|
||||
|
@ -1081,14 +1094,14 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
|
|||
node.attrs.nolabel = '1';
|
||||
}
|
||||
}
|
||||
var widget = new (this.view.registry.get_any(
|
||||
[node.attrs.widget, type.type, node.tag])) (this.view, node);
|
||||
var widget = instanciate_widget(this.view.registry.get_any(
|
||||
[node.attrs.widget, type.type, node.tag]), this.view, node);
|
||||
if (node.tag == 'field') {
|
||||
if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
|
||||
this.view.default_focus_field = widget;
|
||||
}
|
||||
if (node.attrs.nolabel != '1') {
|
||||
var label = new (this.view.registry.get_object('label')) (this.view, node);
|
||||
var label = instanciate_widget(this.view.registry.get_object('label'), this.view, node);
|
||||
label["for"] = widget;
|
||||
this.add_widget(label, widget.colspan + 1);
|
||||
}
|
||||
|
@ -1110,23 +1123,31 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
|
||||
template: 'WidgetGroup'
|
||||
form_template: 'WidgetGroup'
|
||||
}),
|
||||
|
||||
openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetNotebook',
|
||||
form_template: 'WidgetNotebook',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.pages = [];
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
var n = node.children[i];
|
||||
if (n.tag == "page") {
|
||||
var page = new (this.view.registry.get_object('notebookpage'))(
|
||||
var page = instanciate_widget(this.view.registry.get_object('notebookpage'),
|
||||
this.view, n, this, this.pages.length);
|
||||
this.pages.push(page);
|
||||
}
|
||||
}
|
||||
},
|
||||
renderElement: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
_.each(this.pages, function(page) {
|
||||
page.$element = self.$element.find('.' + page.element_class);
|
||||
page.renderElement();
|
||||
});
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -1146,6 +1167,9 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
|||
gravity: 's'
|
||||
});
|
||||
}
|
||||
_.each(this.pages, function(page) {
|
||||
page.start();
|
||||
});
|
||||
},
|
||||
do_select_first_visible_tab: function() {
|
||||
for (var i = 0; i < this.pages.length; i++) {
|
||||
|
@ -1159,7 +1183,7 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
||||
template: 'WidgetNotebookPage',
|
||||
form_template: 'WidgetNotebookPage',
|
||||
init: function(view, node, notebook, index) {
|
||||
this.notebook = notebook;
|
||||
this.index = index;
|
||||
|
@ -1181,7 +1205,7 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetSeparator',
|
||||
form_template: 'WidgetSeparator',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.orientation = node.attrs.orientation || 'horizontal';
|
||||
|
@ -1193,7 +1217,7 @@ openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetButton',
|
||||
form_template: 'WidgetButton',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.force_disabled = false;
|
||||
|
@ -1227,7 +1251,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
|||
var exec_action = function() {
|
||||
if (self.node.attrs.confirm) {
|
||||
var def = $.Deferred();
|
||||
var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
|
||||
var dialog = openerp.web.dialog($('<div>' + self.node.attrs.confirm + '</div>'), {
|
||||
title: _t('Confirm'),
|
||||
modal: true,
|
||||
buttons: [
|
||||
|
@ -1283,7 +1307,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetLabel',
|
||||
form_template: 'WidgetLabel',
|
||||
init: function(view, node) {
|
||||
this.element_name = 'label_' + node.attrs.name;
|
||||
|
||||
|
@ -1295,7 +1319,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
|||
}
|
||||
|
||||
if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
|
||||
this.template = "WidgetParagraph";
|
||||
this.form_template = "WidgetParagraph";
|
||||
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
|
||||
// Widgets default to right-aligned, but paragraph defaults to
|
||||
// left-aligned
|
||||
|
@ -1311,12 +1335,15 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
|||
this.nowrap = true;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
renderElement: function() {
|
||||
var rendered;
|
||||
if (this['for'] && this.type !== 'label') {
|
||||
return QWeb.render(this.template, {widget: this['for']});
|
||||
rendered = QWeb.render(this.form_template, {widget: this['for']});
|
||||
} else {
|
||||
// Actual label widgets should not have a false and have type label
|
||||
rendered = QWeb.render(this.form_template, {widget: this});
|
||||
}
|
||||
// Actual label widgets should not have a false and have type label
|
||||
return QWeb.render(this.template, {widget: this});
|
||||
this.$element.html(rendered);
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
|
@ -1332,6 +1359,58 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface to be implemented by fields.
|
||||
*
|
||||
* Novajs Attributes:
|
||||
* - ...
|
||||
*
|
||||
* Novajs Events:
|
||||
* - ...
|
||||
*
|
||||
*/
|
||||
openerp.web.form.FieldInterface = {
|
||||
/**
|
||||
* Called by the form view to indicate the value of the field.
|
||||
*
|
||||
* set_value() may return an object that can be passed to $.when() that represents the moment when
|
||||
* the field has finished all operations necessary before the user can effectively use the widget.
|
||||
*
|
||||
* Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
|
||||
* regardless of any asynchronous operation currently running and the status of any promise that a
|
||||
* previous call to set_value() could have returned.
|
||||
*
|
||||
* set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
|
||||
* osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
|
||||
* also be able to handle any other format commonly used in the _defaults key on the models in the addons
|
||||
* as well as any format commonly returned in a on_change. It must be able to autodetect those formats as
|
||||
* no information is ever given to know which format is used.
|
||||
*/
|
||||
set_value: function(value) {},
|
||||
/**
|
||||
* Get the current value of the widget.
|
||||
*
|
||||
* Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
|
||||
* the OpenERP server, although it is not assumed to respect the constraints applied to the field.
|
||||
* For example if the field is marqued as "required", a call to get_value() can return false.
|
||||
*
|
||||
* get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
|
||||
* return a defaut value according to the type of field.
|
||||
*
|
||||
* This method is always assumed to perform synchronously, it can not return a promise.
|
||||
*
|
||||
* If there was no user interaction to modify the value of the field, it is always assumed that
|
||||
* get_value() return the same semantic value than the one passed in the last call to set_value(),
|
||||
* altough the syntax can be different. This can be the case for type of fields that have a different
|
||||
* syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
|
||||
*/
|
||||
get_value: function() {},
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class for classes implementing FieldInterface. Should be renamed to AbstractField some
|
||||
* day.
|
||||
*/
|
||||
openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
|
||||
/**
|
||||
* @constructs openerp.web.form.Field
|
||||
|
@ -1448,7 +1527,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
|||
});
|
||||
|
||||
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||
template: 'FieldChar',
|
||||
form_template: 'FieldChar',
|
||||
init: function (view, node) {
|
||||
this._super(view, node);
|
||||
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
|
||||
|
@ -1493,7 +1572,7 @@ openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
||||
template: 'FieldEmail',
|
||||
form_template: 'FieldEmail',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
|
@ -1508,7 +1587,7 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
|
||||
template: 'FieldUrl',
|
||||
form_template: 'FieldUrl',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
|
@ -1526,16 +1605,11 @@ openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
|
|||
init: function (view, node) {
|
||||
this._super(view, node);
|
||||
if (node.attrs.digits) {
|
||||
this.parse_digits(node.attrs.digits);
|
||||
this.digits = py.eval(node.attrs.digits).toJSON();
|
||||
} else {
|
||||
this.digits = view.fields_view.fields[node.attrs.name].digits;
|
||||
}
|
||||
},
|
||||
parse_digits: function (digits_attr) {
|
||||
// could use a Python parser instead.
|
||||
var match = /^\s*[\(\[](\d+),\s*(\d+)/.exec(digits_attr);
|
||||
return [parseInt(match[1], 10), parseInt(match[2], 10)];
|
||||
},
|
||||
set_value: function(value) {
|
||||
if (value === false || value === undefined) {
|
||||
// As in GTK client, floats default to 0
|
||||
|
@ -1631,7 +1705,7 @@ openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||
template: "EmptyComponent",
|
||||
form_template: "EmptyComponent",
|
||||
build_widget: function() {
|
||||
return new openerp.web.DateTimeWidget(this);
|
||||
},
|
||||
|
@ -1668,7 +1742,7 @@ openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
||||
template: 'FieldText',
|
||||
form_template: 'FieldText',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('textarea').change(this.on_ui_change);
|
||||
|
@ -1729,7 +1803,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
||||
template: 'FieldBoolean',
|
||||
form_template: 'FieldBoolean',
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -1753,7 +1827,7 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
|
||||
template: 'FieldProgressBar',
|
||||
form_template: 'FieldProgressBar',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('div').progressbar({
|
||||
|
@ -1777,7 +1851,7 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
||||
template: 'FieldSelection',
|
||||
form_template: 'FieldSelection',
|
||||
init: function(view, node) {
|
||||
var self = this;
|
||||
this._super(view, node);
|
||||
|
@ -1883,7 +1957,7 @@ openerp.web.form.dialog = function(content, options) {
|
|||
};
|
||||
|
||||
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2One',
|
||||
form_template: 'FieldMany2One',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.limit = 7;
|
||||
|
@ -1912,9 +1986,13 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
|||
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
|
||||
var bindings = {};
|
||||
bindings[self.cm_id + "_search"] = function() {
|
||||
if (self.readonly)
|
||||
return;
|
||||
self._search_create_popup("search");
|
||||
};
|
||||
bindings[self.cm_id + "_create"] = function() {
|
||||
if (self.readonly)
|
||||
return;
|
||||
self._search_create_popup("form");
|
||||
};
|
||||
bindings[self.cm_id + "_open"] = function() {
|
||||
|
@ -2019,6 +2097,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
|||
minLength: 0,
|
||||
delay: 0
|
||||
});
|
||||
this.$input.autocomplete("widget").addClass("openerp");
|
||||
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
||||
this.$input.keyup(function(e) {
|
||||
if (e.which === 13) {
|
||||
|
@ -2201,6 +2280,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
|||
this.$input.prop('readonly', this.readonly);
|
||||
}
|
||||
});
|
||||
openerp.web.check_interface(openerp.web.form.FieldMany2One, openerp.web.form.FieldInterface);
|
||||
|
||||
/*
|
||||
# Values: (0, 0, { fields }) create
|
||||
|
@ -2248,7 +2328,7 @@ var commands = {
|
|||
}
|
||||
};
|
||||
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldOne2Many',
|
||||
form_template: 'FieldOne2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
|
@ -2522,7 +2602,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
|||
this.previous_readonly = this.readonly;
|
||||
if (this.viewmanager) {
|
||||
this.is_loaded = this.is_loaded.pipe(function() {
|
||||
self.viewmanager.stop();
|
||||
self.viewmanager.destroy();
|
||||
return $.when(self.load_views()).then(function() {
|
||||
self.reload_current_view();
|
||||
});
|
||||
|
@ -2622,7 +2702,7 @@ openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2Many',
|
||||
form_template: 'FieldMany2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
|
@ -2702,7 +2782,7 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
|
|||
this.previous_readonly = this.readonly;
|
||||
if (this.list_view) {
|
||||
this.is_loaded = this.is_loaded.pipe(function() {
|
||||
self.list_view.stop();
|
||||
self.list_view.destroy();
|
||||
return $.when(self.load_view()).then(function() {
|
||||
self.reload_content();
|
||||
});
|
||||
|
@ -2750,7 +2830,7 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open
|
|||
var pop = new openerp.web.form.FormOpenPopup(this);
|
||||
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
|
||||
title: _t("Open: ") + this.name,
|
||||
readonly: this.widget_parent.is_readonly()
|
||||
readonly: this.getParent().is_readonly()
|
||||
});
|
||||
pop.on_write_completed.add_last(function() {
|
||||
self.reload_content();
|
||||
|
@ -2787,7 +2867,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
}, read_function: null});
|
||||
this.initial_ids = this.options.initial_ids;
|
||||
this.created_elements = [];
|
||||
this.render_element();
|
||||
this.renderElement();
|
||||
openerp.web.form.dialog(this.$element, {
|
||||
close: function() {
|
||||
self.check_exit();
|
||||
|
@ -2831,10 +2911,14 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
this.new_object();
|
||||
}
|
||||
},
|
||||
stop: function () {
|
||||
this.$element.dialog('close');
|
||||
this._super();
|
||||
},
|
||||
setup_search_view: function(search_defaults) {
|
||||
var self = this;
|
||||
if (this.searchview) {
|
||||
this.searchview.stop();
|
||||
this.searchview.destroy();
|
||||
}
|
||||
this.searchview = new openerp.web.SearchView(this,
|
||||
this.dataset, false, search_defaults);
|
||||
|
@ -2864,7 +2948,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
|
||||
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
|
||||
$cbutton.click(function() {
|
||||
self.stop();
|
||||
self.destroy();
|
||||
});
|
||||
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
|
||||
if(self.options.disable_multiple_selection) {
|
||||
|
@ -2872,7 +2956,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
}
|
||||
$sbutton.click(function() {
|
||||
self.on_select_elements(self.selected_ids);
|
||||
self.stop();
|
||||
self.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2956,7 +3040,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
if (this.created_elements.length > 0) {
|
||||
this.on_select_elements(this.created_elements);
|
||||
}
|
||||
this.stop();
|
||||
this.destroy();
|
||||
},
|
||||
on_default_get: function(res) {}
|
||||
});
|
||||
|
@ -2967,7 +3051,7 @@ openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
|
|||
},
|
||||
select_record: function(index) {
|
||||
this.popup.on_select_elements([this.dataset.ids[index]]);
|
||||
this.popup.stop();
|
||||
this.popup.destroy();
|
||||
},
|
||||
do_select: function(ids, records) {
|
||||
this._super(ids, records);
|
||||
|
@ -2996,8 +3080,8 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
|||
this.row_id = row_id;
|
||||
this.context = context || {};
|
||||
this.options = _.defaults(options || {}, {"auto_write": true});
|
||||
this.render_element();
|
||||
this.$element.dialog({
|
||||
this.renderElement();
|
||||
openerp.web.dialog(this.$element, {
|
||||
title: options.title || '',
|
||||
modal: true,
|
||||
width: 960,
|
||||
|
@ -3043,12 +3127,12 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
|||
var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
|
||||
$nbutton.click(function() {
|
||||
self.view_form.do_save().then(function() {
|
||||
self.stop();
|
||||
self.destroy();
|
||||
});
|
||||
});
|
||||
var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
|
||||
$cbutton.click(function() {
|
||||
self.stop();
|
||||
self.destroy();
|
||||
});
|
||||
if (self.options.readonly) {
|
||||
$nbutton.hide();
|
||||
|
@ -3071,7 +3155,7 @@ openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
||||
template: 'FieldReference',
|
||||
form_template: 'FieldReference',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.fields_view = {
|
||||
|
@ -3086,7 +3170,7 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
|||
};
|
||||
this.get_fields_values = view.get_fields_values;
|
||||
this.get_selected_ids = view.get_selected_ids;
|
||||
this.do_onchange = this.on_form_changed = this.on_nop;
|
||||
this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
|
||||
this.dataset = this.view.dataset;
|
||||
this.widgets_counter = 0;
|
||||
this.view_id = 'reference_' + _.uniqueId();
|
||||
|
@ -3117,7 +3201,11 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
|||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
|
||||
this.selection.renderElement();
|
||||
this.selection.start();
|
||||
this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
|
||||
this.m2o.renderElement();
|
||||
this.m2o.start();
|
||||
},
|
||||
is_valid: function() {
|
||||
|
@ -3226,7 +3314,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
||||
template: 'FieldBinaryFile',
|
||||
form_template: 'FieldBinaryFile',
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
|
||||
|
@ -3264,7 +3352,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
||||
template: 'FieldBinaryImage',
|
||||
form_template: 'FieldBinaryImage',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$image = this.$element.find('img.oe-binary-image');
|
||||
|
@ -3276,8 +3364,14 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
|||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
this.set_image_maxwidth();
|
||||
var url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
|
||||
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
|
||||
|
||||
var url;
|
||||
if (value && value.substr(0, 10).indexOf(' ') == -1) {
|
||||
url = 'data:image/png;base64,' + this.value;
|
||||
} else {
|
||||
url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
|
||||
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
|
||||
}
|
||||
this.$image.attr('src', url);
|
||||
},
|
||||
set_image_maxwidth: function() {
|
||||
|
@ -3299,7 +3393,7 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
||||
template: "EmptyComponent",
|
||||
form_template: "EmptyComponent",
|
||||
start: function() {
|
||||
this._super();
|
||||
this.selected_value = null;
|
||||
|
@ -3364,10 +3458,11 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
|
||||
render: function () {
|
||||
renderElement: function () {
|
||||
var $root = $('<div class="oe_form_html_view">');
|
||||
this.render_children(this, $root);
|
||||
return $root.html();
|
||||
var rendered = $root.html();
|
||||
this.$element.html(rendered);
|
||||
},
|
||||
render_children: function (object, $into) {
|
||||
var self = this,
|
||||
|
@ -3376,10 +3471,9 @@ openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
|
|||
if (typeof child === 'string') {
|
||||
$into.text(child);
|
||||
} else if (child.tag === 'field') {
|
||||
$into.append(
|
||||
new (self.view.registry.get_object('frame'))(
|
||||
self.view, {tag: 'ueule', attrs: {}, children: [child] })
|
||||
.render());
|
||||
var widget = instanciate_widget(self.view.registry.get_object('frame'),
|
||||
self.view, {tag: 'ueule', attrs: {}, children: [child] });
|
||||
widget.appendTo($into);
|
||||
} else {
|
||||
var $child = $(document.createElement(child.tag))
|
||||
.attr(child.attrs)
|
||||
|
@ -3426,6 +3520,14 @@ openerp.web.form.widgets = new openerp.web.Registry({
|
|||
'html': 'openerp.web.form.WidgetHtml'
|
||||
});
|
||||
|
||||
var instanciate_widget = function(claz, view, node, o1, o2) {
|
||||
var widget = new (claz)(view, node, o1, o2);
|
||||
widget.element_class = (['formview', view.view_id, widget.element_name,
|
||||
view.widgets_counter++].join("_")).replace(/[^\r\n\f0-9A-Za-z_-]/g, "_");
|
||||
view.widgets[widget.element_class] = widget;
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
if (this._limit === undefined) {
|
||||
this._limit = (this.options.limit
|
||||
|| this.defaults.limit
|
||||
|| (this.widget_parent.action || {}).limit
|
||||
|| (this.getParent().action || {}).limit
|
||||
|| 80);
|
||||
}
|
||||
return this._limit;
|
||||
|
@ -158,7 +158,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
var pair = this.colors[i],
|
||||
color = pair[0],
|
||||
expression = pair[1];
|
||||
if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
|
||||
if (py.evaluate(expression, context).toJSON()) {
|
||||
return 'color: ' + color + ';';
|
||||
}
|
||||
// TODO: handle evaluation errors
|
||||
|
@ -291,7 +291,10 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
*/
|
||||
configure_pager: function (dataset) {
|
||||
this.dataset.ids = dataset.ids;
|
||||
this.dataset._length = dataset._length;
|
||||
// Not exactly clean
|
||||
if (dataset._length) {
|
||||
this.dataset._length = dataset._length;
|
||||
}
|
||||
|
||||
var limit = this.limit(),
|
||||
total = dataset.size(),
|
||||
|
@ -360,7 +363,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
_(this.fields_view.arch.children).map(field_to_column));
|
||||
if (grouped) {
|
||||
this.columns.unshift({
|
||||
id: '_group', tag: '', string: "Group", meta: true,
|
||||
id: '_group', tag: '', string: _t("Group"), meta: true,
|
||||
modifiers_for: function () { return {}; }
|
||||
}, {
|
||||
id: '_count', tag: '', string: '#', meta: true,
|
||||
|
@ -541,19 +544,17 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
* @param {Array} records selected record values
|
||||
*/
|
||||
do_select: function (ids, records) {
|
||||
this.$element.find('.oe-list-delete')
|
||||
.attr('disabled', !ids.length);
|
||||
if (this.sidebar) {
|
||||
if (ids.length) {
|
||||
this.sidebar.do_unfold();
|
||||
} else {
|
||||
this.sidebar.do_fold();
|
||||
}
|
||||
}
|
||||
if (!records.length) {
|
||||
this.$element.find('.oe-list-delete').attr('disabled', !ids.length);
|
||||
if (!ids.length) {
|
||||
this.dataset.index = 0;
|
||||
if (this.sidebar) { this.sidebar.do_fold(); }
|
||||
this.compute_aggregates();
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
|
||||
if (this.sidebar) { this.sidebar.do_unfold(); }
|
||||
|
||||
this.compute_aggregates(_(records).map(function (record) {
|
||||
return {count: 1, values: record};
|
||||
}));
|
||||
|
@ -1260,9 +1261,11 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
|
|||
if (column.meta) {
|
||||
// do not do anything
|
||||
} else if (column.id in group.aggregates) {
|
||||
var value = group.aggregates[column.id];
|
||||
var r = {};
|
||||
r[column.id] = {value: group.aggregates[column.id]};
|
||||
$('<td class="oe-number">')
|
||||
.html(openerp.web.format_value(value, column))
|
||||
.html(openerp.web.format_cell(
|
||||
r, column, {process_modifiers: false}))
|
||||
.appendTo($row);
|
||||
} else {
|
||||
$row.append('<td>');
|
||||
|
|
|
@ -139,7 +139,7 @@ openerp.web.list_editable = function (openerp) {
|
|||
}
|
||||
cancelled.then(function () {
|
||||
self.view.unpad_columns();
|
||||
self.edition_form.stop();
|
||||
self.edition_form.destroy();
|
||||
self.edition_form.$element.remove();
|
||||
delete self.edition_form;
|
||||
delete self.edition_id;
|
||||
|
@ -235,26 +235,16 @@ openerp.web.list_editable = function (openerp) {
|
|||
}
|
||||
self.edition = true;
|
||||
self.edition_id = record_id;
|
||||
self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), {
|
||||
form_template: 'ListView.row.form',
|
||||
registry: openerp.web.list.form.widgets,
|
||||
$element: $new_row
|
||||
});
|
||||
// HA HA
|
||||
self.edition_form.appendTo();
|
||||
self.edition_form = new openerp.web.ListEditableFormView(self.view, self.dataset, false);
|
||||
self.edition_form.$element = $new_row;
|
||||
self.edition_form.editable_list = self;
|
||||
// HO HO
|
||||
// empty
|
||||
$.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
|
||||
// put in $.when just in case FormView.on_loaded becomes asynchronous
|
||||
$new_row.find('> td')
|
||||
.addClass('oe-field-cell')
|
||||
.removeAttr('width')
|
||||
.end()
|
||||
.find('td:last').removeClass('oe-field-cell').end();
|
||||
if (self.options.selectable) {
|
||||
$new_row.prepend('<th>');
|
||||
}
|
||||
if (self.options.isClarkGable) {
|
||||
$new_row.prepend('<th>');
|
||||
}
|
||||
// pad in case of groupby
|
||||
_(self.columns).each(function (column) {
|
||||
if (column.meta) {
|
||||
|
@ -383,7 +373,7 @@ openerp.web.list_editable = function (openerp) {
|
|||
openerp.web.list.form = {};
|
||||
}
|
||||
openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
|
||||
template: 'ListView.row.frame'
|
||||
form_template: 'ListView.row.frame'
|
||||
});
|
||||
var form_widgets = openerp.web.form.widgets;
|
||||
openerp.web.list.form.widgets = form_widgets.extend({
|
||||
|
@ -418,9 +408,10 @@ openerp.web.list_editable = function (openerp) {
|
|||
});
|
||||
|
||||
openerp.web.ListEditableFormView = openerp.web.FormView.extend({
|
||||
init_view: function() {},
|
||||
_render_and_insert: function () {
|
||||
return this.start();
|
||||
}
|
||||
form_template: 'ListView.row.form',
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.registry = openerp.web.list.form.widgets;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -68,13 +68,13 @@ openerp.web.page = function (openerp) {
|
|||
openerp.web.page = {};
|
||||
|
||||
openerp.web.page.WidgetFrameReadonly = openerp.web.form.WidgetFrame.extend({
|
||||
template: 'WidgetFrame.readonly'
|
||||
form_template: 'WidgetFrame.readonly'
|
||||
});
|
||||
openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({
|
||||
|
||||
});
|
||||
openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
form_template: 'FieldChar.readonly',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
|
||||
|
@ -89,13 +89,27 @@ openerp.web.page = function (openerp) {
|
|||
return show_value;
|
||||
}
|
||||
});
|
||||
openerp.web.page.FieldFloatReadonly = openerp.web.page.FieldCharReadonly.extend({
|
||||
init: function (view, node) {
|
||||
this._super(view, node);
|
||||
if (node.attrs.digits) {
|
||||
this.digits = py.eval(node.attrs.digits).toJSON();
|
||||
} else {
|
||||
this.digits = view.fields_view.fields[node.attrs.name].digits;
|
||||
}
|
||||
}
|
||||
});
|
||||
openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({
|
||||
template: 'FieldURI.readonly',
|
||||
form_template: 'FieldURI.readonly',
|
||||
scheme: null,
|
||||
format_value: function (value) {
|
||||
return value;
|
||||
},
|
||||
set_value: function (value) {
|
||||
if (!value) {
|
||||
this.$element.find('a').text('').attr('href', '#');
|
||||
return;
|
||||
}
|
||||
this.$element.find('a')
|
||||
.attr('href', this.scheme + ':' + value)
|
||||
.text(this.format_value(value));
|
||||
|
@ -106,6 +120,10 @@ openerp.web.page = function (openerp) {
|
|||
});
|
||||
openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({
|
||||
set_value: function (value) {
|
||||
if (!value) {
|
||||
this.$element.find('a').text('').attr('href', '#');
|
||||
return;
|
||||
}
|
||||
var s = /(\w+):(.+)/.exec(value);
|
||||
if (!s) {
|
||||
value = "http://" + value;
|
||||
|
@ -120,7 +138,7 @@ openerp.web.page = function (openerp) {
|
|||
}
|
||||
});
|
||||
openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
form_template: 'FieldChar.readonly',
|
||||
init: function(view, node) {
|
||||
// lifted straight from r/w version
|
||||
var self = this;
|
||||
|
@ -225,7 +243,7 @@ openerp.web.page = function (openerp) {
|
|||
}
|
||||
});
|
||||
openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({
|
||||
template: 'FieldURI.readonly',
|
||||
form_template: 'FieldURI.readonly',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
var self = this;
|
||||
|
@ -261,7 +279,7 @@ openerp.web.page = function (openerp) {
|
|||
'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly',
|
||||
'reference': 'openerp.web.page.FieldReferenceReadonly',
|
||||
'boolean': 'openerp.web.page.FieldBooleanReadonly',
|
||||
'float': 'openerp.web.page.FieldCharReadonly',
|
||||
'float': 'openerp.web.page.FieldFloatReadonly',
|
||||
'integer': 'openerp.web.page.FieldCharReadonly',
|
||||
'float_time': 'openerp.web.page.FieldCharReadonly',
|
||||
'binary': 'openerp.web.page.FieldBinaryFileReadonly',
|
||||
|
|
|
@ -146,7 +146,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
|
|||
var pair = this.colors[i],
|
||||
color = pair[0],
|
||||
expression = pair[1];
|
||||
if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
|
||||
if (py.evaluate(expression, context).toJSON()) {
|
||||
return 'color: ' + color + ';';
|
||||
}
|
||||
// TODO: handle evaluation errors
|
||||
|
@ -224,21 +224,25 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
|
|||
active_model: self.dataset.model,
|
||||
active_id: id,
|
||||
active_ids: [id]};
|
||||
this.rpc('/web/treeview/action', {
|
||||
return this.rpc('/web/treeview/action', {
|
||||
id: id,
|
||||
model: this.dataset.model,
|
||||
context: new openerp.web.CompoundContext(
|
||||
this.dataset.get_context(), local_context)
|
||||
}, function (actions) {
|
||||
}).pipe(function (actions) {
|
||||
if (!actions.length) { return; }
|
||||
var action = actions[0][2];
|
||||
var c = new openerp.web.CompoundContext(local_context);
|
||||
if (action.context) {
|
||||
c.add(action.context);
|
||||
}
|
||||
action.context = c;
|
||||
self.do_action(action);
|
||||
});
|
||||
return self.rpc('/web/session/eval_domain_and_context', {
|
||||
contexts: [c], domains: []
|
||||
}).pipe(function (res) {
|
||||
action.context = res.context;
|
||||
return self.do_action(action);
|
||||
}, null);
|
||||
}, null);
|
||||
},
|
||||
|
||||
// show & hide the contents
|
||||
|
|
|
@ -30,31 +30,31 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
},
|
||||
dialog_stop: function () {
|
||||
if (this.dialog) {
|
||||
this.dialog_viewmanager.stop();
|
||||
this.dialog_viewmanager.destroy();
|
||||
this.dialog_viewmanager = null;
|
||||
this.dialog.stop();
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
},
|
||||
content_stop: function () {
|
||||
if (this.inner_viewmanager) {
|
||||
this.inner_viewmanager.stop();
|
||||
this.inner_viewmanager.destroy();
|
||||
this.inner_viewmanager = null;
|
||||
}
|
||||
if (this.client_widget) {
|
||||
this.client_widget.stop();
|
||||
this.client_widget.destroy();
|
||||
this.client_widget = null;
|
||||
}
|
||||
},
|
||||
do_push_state: function(state) {
|
||||
if (this.widget_parent && this.widget_parent.do_push_state) {
|
||||
if (this.getParent() && this.getParent().do_push_state) {
|
||||
if (this.inner_action) {
|
||||
state['model'] = this.inner_action.res_model;
|
||||
if (this.inner_action.id) {
|
||||
state['action_id'] = this.inner_action.id;
|
||||
}
|
||||
}
|
||||
this.widget_parent.do_push_state(state);
|
||||
this.getParent().do_push_state(state);
|
||||
}
|
||||
},
|
||||
do_load_state: function(state, warm) {
|
||||
|
@ -65,6 +65,9 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
if (run_action) {
|
||||
this.null_action();
|
||||
action_loaded = this.do_action(state.action_id);
|
||||
session.webclient.menu.has_been_loaded.then(function() {
|
||||
session.webclient.menu.open_action(state.action_id);
|
||||
});
|
||||
}
|
||||
} else if (state.model && state.id) {
|
||||
// TODO handle context & domain ?
|
||||
|
@ -142,7 +145,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
if(on_close)
|
||||
this.dialog.on_close.add(on_close);
|
||||
} else {
|
||||
this.dialog_viewmanager.stop();
|
||||
this.dialog_viewmanager.destroy();
|
||||
}
|
||||
this.dialog.dialog_title = action.name;
|
||||
this.dialog_viewmanager = new session.web.ViewManagerAction(this, action);
|
||||
|
@ -150,7 +153,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
this.dialog.open();
|
||||
} else {
|
||||
if(action.menu_id) {
|
||||
return this.widget_parent.do_action(action, function () {
|
||||
return this.getParent().do_action(action, function () {
|
||||
session.webclient.menu.open_menu(action.menu_id);
|
||||
});
|
||||
}
|
||||
|
@ -180,7 +183,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
this.content_stop();
|
||||
this.dialog_stop();
|
||||
var ClientWidget = session.web.client_actions.get_object(action.tag);
|
||||
(this.client_widget = new ClientWidget(this, action.params)).appendTo(this);
|
||||
(this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element);
|
||||
},
|
||||
ir_actions_report_xml: function(action, on_closed) {
|
||||
var self = this;
|
||||
|
@ -209,7 +212,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
|
|||
window.open(action.url, action.target === 'self' ? '_self' : '_blank');
|
||||
},
|
||||
ir_ui_menu: function (action) {
|
||||
this.widget_parent.do_action(action);
|
||||
this.getParent().do_action(action);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -385,7 +388,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
|
|||
setup_search_view: function(view_id, search_defaults) {
|
||||
var self = this;
|
||||
if (this.searchview) {
|
||||
this.searchview.stop();
|
||||
this.searchview.destroy();
|
||||
}
|
||||
this.searchview = new session.web.SearchView(
|
||||
this, this.dataset,
|
||||
|
@ -408,6 +411,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
|
|||
var groupby = results.group_by.length
|
||||
? results.group_by
|
||||
: action_context.group_by;
|
||||
if (_.isString(groupby)) {
|
||||
groupby = [groupby];
|
||||
}
|
||||
controller.do_search(results.domain, results.context, groupby || []);
|
||||
});
|
||||
},
|
||||
|
@ -680,9 +686,9 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
|
|||
});
|
||||
},
|
||||
do_push_state: function(state) {
|
||||
if (this.widget_parent && this.widget_parent.do_push_state) {
|
||||
if (this.getParent() && this.getParent().do_push_state) {
|
||||
state["view_type"] = this.active_view;
|
||||
this.widget_parent.do_push_state(state);
|
||||
this.getParent().do_push_state(state);
|
||||
}
|
||||
},
|
||||
do_load_state: function(state, warm) {
|
||||
|
@ -702,7 +708,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
|
|||
},
|
||||
shortcut_check : function(view) {
|
||||
var self = this;
|
||||
var grandparent = this.widget_parent && this.widget_parent.widget_parent;
|
||||
var grandparent = this.getParent() && this.getParent().getParent();
|
||||
// display shortcuts if on the first view for the action
|
||||
var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle');
|
||||
if (!this.action.name ||
|
||||
|
@ -796,8 +802,8 @@ session.web.Sidebar = session.web.OldWidget.extend({
|
|||
},
|
||||
add_default_sections: function() {
|
||||
var self = this,
|
||||
view = this.widget_parent,
|
||||
view_manager = view.widget_parent,
|
||||
view = this.getParent(),
|
||||
view_manager = view.getParent(),
|
||||
action = view_manager.action;
|
||||
if (this.session.uid === 1) {
|
||||
this.add_section(_t('Customize'), 'customize');
|
||||
|
@ -912,11 +918,11 @@ session.web.Sidebar = session.web.OldWidget.extend({
|
|||
},
|
||||
on_item_action_clicked: function(item) {
|
||||
var self = this;
|
||||
self.widget_parent.sidebar_context().then(function (context) {
|
||||
var ids = self.widget_parent.get_selected_ids();
|
||||
self.getParent().sidebar_context().then(function (context) {
|
||||
var ids = self.getParent().get_selected_ids();
|
||||
if (ids.length == 0) {
|
||||
//TODO: make prettier warning?
|
||||
$("<div />").text(_t("You must choose at least one record.")).dialog({
|
||||
openerp.web.dialog($("<div />").text(_t("You must choose at least one record.")), {
|
||||
title: _t("Warning"),
|
||||
modal: true
|
||||
});
|
||||
|
@ -925,7 +931,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
|
|||
var additional_context = _.extend({
|
||||
active_id: ids[0],
|
||||
active_ids: ids,
|
||||
active_model: self.widget_parent.dataset.model
|
||||
active_model: self.getParent().dataset.model
|
||||
}, context);
|
||||
self.rpc("/web/action/load", {
|
||||
action_id: item.action.id,
|
||||
|
@ -937,7 +943,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
|
|||
result.result.flags.new_window = true;
|
||||
self.do_action(result.result, function () {
|
||||
// reload view
|
||||
self.widget_parent.reload();
|
||||
self.getParent().reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1111,8 +1117,8 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
|
|||
var self = this;
|
||||
var result_handler = function () {
|
||||
if (on_closed) { on_closed.apply(null, arguments); }
|
||||
if (self.widget_parent && self.widget_parent.on_action_executed) {
|
||||
return self.widget_parent.on_action_executed.apply(null, arguments);
|
||||
if (self.getParent() && self.getParent().on_action_executed) {
|
||||
return self.getParent().on_action_executed.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
||||
|
@ -1184,8 +1190,8 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
|
|||
this.$element.hide();
|
||||
},
|
||||
do_push_state: function(state) {
|
||||
if (this.widget_parent && this.widget_parent.do_push_state) {
|
||||
this.widget_parent.do_push_state(state);
|
||||
if (this.getParent() && this.getParent().do_push_state) {
|
||||
this.getParent().do_push_state(state);
|
||||
}
|
||||
},
|
||||
do_load_state: function(state, warm) {
|
||||
|
|
|
@ -17,35 +17,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Interface">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
|
||||
<tr>
|
||||
<td colspan="2" valign="top">
|
||||
<div id="oe_header" class="header"></div>
|
||||
<div id="oe_menu" class="menu"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" valign="top" height="100%">
|
||||
<table cellspacing="0" cellpadding="0" border="0" height="100%" width="100%">
|
||||
<tr>
|
||||
<td valign="top" id="oe_secondary_menu" class="secondary_menu"></td>
|
||||
<td valign="top" class="oe-application-container">
|
||||
<div id="oe_app" class="oe-application">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div id="oe_footer" class="oe_footer">
|
||||
<p class="oe_footer_powered">Powered by <a href="http://www.openerp.com">OpenERP</a></p>
|
||||
<t t-name="WebClient">
|
||||
<div class="oe_webclient">
|
||||
<div class="oe_topbar">
|
||||
<div class="oe_menu_placeholder"/>
|
||||
<div class="oe_user_menu_placeholder"/>
|
||||
<div class="oe_systray"/>
|
||||
</div>
|
||||
<div class="oe_leftbar">
|
||||
<a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
|
||||
|
||||
<div class="oe_secondary_menus_container"/>
|
||||
|
||||
<div class="oe_footer">
|
||||
Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="oe_application"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Loading">
|
||||
<div id="oe_loading">
|
||||
|
@ -154,7 +143,7 @@
|
|||
<option t-att-value="db"><t t-esc="db"/></option>
|
||||
</t>
|
||||
</select>
|
||||
<input t-if="!db_list" name="drop_db" class="required"
|
||||
<input t-if="!db_list" name="backup_db" class="required"
|
||||
type="text" autofocus="autofocus"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -335,81 +324,73 @@
|
|||
<div id="oe_db_options" class="oe_db_options"></div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Header">
|
||||
<div>
|
||||
<a t-att-href="'/' + widget.qs" class="company_logo_link">
|
||||
<div class="company_logo" />
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Header-content">
|
||||
<h1 class="header_title">
|
||||
<t t-esc="user.company_id[1]"/> (<t t-esc="widget.session.db"/>)<br/>
|
||||
<small class="username"><t t-esc="user.name"/></small>
|
||||
</h1>
|
||||
<div class="header_corner">
|
||||
<ul class="block">
|
||||
<li>
|
||||
<a t-att-href="'/' + widget.qs" title="Home" class="home"><img t-att-src='_s + "/web/static/src/img/header-home.png"' width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
<li class="preferences">
|
||||
<a href="javascript:void(0)" title="Preferences" class="preferences"><img t-att-src='_s + "/web/static/src/img/header-preferences.png"' width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:void(0)" title="About" class="about"><img t-att-src='_s + "/web/static/src/img/header-about.png"' width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="block">
|
||||
<a href="javascript:void(0)" class="logout">LOGOUT</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="oe-shortcuts"> </div>
|
||||
</t>
|
||||
<ul t-name="Shortcuts">
|
||||
<li t-foreach="shortcuts" t-as="shortcut"
|
||||
t-att-data-id="shortcut.res_id"
|
||||
t-att-data-shortcut-id="shortcut.id"
|
||||
><t t-esc="shortcut.name"/></li>
|
||||
</ul>
|
||||
<t t-name="Menu">
|
||||
<table align="center">
|
||||
<tr>
|
||||
<td t-foreach="widget.data.data.children" t-as="menu">
|
||||
<a href="#" t-att-data-menu="menu.id">
|
||||
<t t-esc="menu.name"/>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul class="oe_menu" t-if="widget.data">
|
||||
<li t-foreach="widget.data.data.children" t-as="menu">
|
||||
<t t-call="Menu.secondary.link"/>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
<t t-name="Menu.secondary">
|
||||
<div t-attf-class="oe_toggle_secondary_menu">
|
||||
<span class="oe_menu_fold" title="Fold menu"><t t-raw="'&laquo;'"/></span>
|
||||
<span class="oe_menu_unfold" title="Unfold menu"><t t-raw="'&raquo;'"/></span>
|
||||
</div>
|
||||
<div t-foreach="widget.data.data.children" t-as="menu" style="display: none" class="oe_secondary_menu" t-att-data-menu-parent="menu.id">
|
||||
<t t-foreach="menu.children" t-as="menu">
|
||||
<t t-set="classname" t-translation="off">oe_secondary_menu_item</t>
|
||||
<t t-set="level" t-value="0"/>
|
||||
<t t-call="Menu.secondary.children"/>
|
||||
<div class="oe_secondary_menu_section">
|
||||
<t t-esc="menu.name"/>
|
||||
<!--
|
||||
Shall the section be still clickable ?
|
||||
<t t-call="Menu.secondary.link"/>
|
||||
-->
|
||||
</div>
|
||||
<t t-call="Menu.secondary.submenu"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Menu.secondary.children">
|
||||
<t t-set="level" t-value="level + 1"/>
|
||||
<a href="#" t-att-id="menu.children.length ? 'menu_' + menu.id : undefined"
|
||||
t-att-class="classname + (menu.children.length ? ' submenu' : ' leaf') + (menu_first and level == 1 ? ' opened' : '')"
|
||||
t-att-data-menu="menu.children.length ? undefined : menu.id">
|
||||
<span t-attf-style="padding-left: #{(level - 2) * 20}px"> <t t-esc="menu.name"/></span>
|
||||
<t t-name="Menu.secondary.submenu">
|
||||
<ul t-if="menu.children.length" class="oe_secondary_submenu">
|
||||
<li t-foreach="menu.children" t-as="menu">
|
||||
<t t-call="Menu.secondary.link"/>
|
||||
<!--<span class="oe_menu_label">8</span>-->
|
||||
<t t-call="Menu.secondary.submenu"/>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
<t t-name="Menu.secondary.link">
|
||||
<a href="#"
|
||||
t-att-class="menu.children.length ? 'oe_menu_toggler' : 'oe_menu_leaf'"
|
||||
t-att-data-menu="menu.id"
|
||||
t-att-data-action-model="menu.action ? menu.action.split(',')[0] : ''"
|
||||
t-att-data-action-id="menu.action ? menu.action.split(',')[1] : ''">
|
||||
<t t-esc="menu.name"/>
|
||||
</a>
|
||||
<div t-attf-class="oe_secondary_submenu" t-if="menu.children.length" t-att-style="menu_first and level == 1 ? undefined : 'display: none'">
|
||||
<t t-foreach="menu.children" t-as="menu">
|
||||
<t t-set="classname" t-translation="off">oe_secondary_submenu_item</t>
|
||||
<t t-call="Menu.secondary.children"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="UserMenu">
|
||||
<ul class="oe_user_menu oe_topbar_item">
|
||||
<li class="oe_dropdown">
|
||||
<a href="#" class="oe_dropdown_toggle">
|
||||
<img class="oe_topbar_avatar" t-att-data-default-src="_s + '/web/static/src/img/topbar-avatar.png'"/>
|
||||
<span class="oe_topbar_name"/>
|
||||
</a>
|
||||
<ul class="oe_dropdown_options">
|
||||
<!--<li><a href="#" data-menu="profile">Profile</a></li>-->
|
||||
<li><a href="#" data-menu="settings">Settings</a></li>
|
||||
<li><a href="#" data-menu="logout">Log out</a></li>
|
||||
<li><hr/></li>
|
||||
<li><a href="#" data-menu="about">About OpenERP</a></li>
|
||||
<li><hr/></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
<t t-name="UserMenu.shortcut">
|
||||
<li t-foreach="shortcuts" t-as="shortcut">
|
||||
<a href="#" data-menu="shortcut" t-att-data-id="shortcut.res_id" t-att-data-shortcut-id="shortcut.id">
|
||||
<t t-esc="shortcut.name"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="ViewManager">
|
||||
<table class="view-manager-main-table" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
|
@ -728,7 +709,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
<t t-name="ListView.row.form">
|
||||
<t t-raw="frame.render()"/>
|
||||
<th t-if="widget.editable_list.options.selectable"></th>
|
||||
<th t-if="widget.editable_list.options.isClarkGable"></th>
|
||||
</t>
|
||||
|
||||
<t t-name="FormView">
|
||||
|
@ -743,7 +725,6 @@
|
|||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<t t-raw="frame.render()"/>
|
||||
</t>
|
||||
<t t-name="One2Many.formview" t-extend="FormView">
|
||||
<t t-jquery=".oe_form_buttons" t-operation="inner">
|
||||
|
@ -892,7 +873,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
<t t-foreach="widget.pages" t-as="page">
|
||||
<t t-raw="page.render()"/>
|
||||
<div t-att-class="page.element_class">
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetNotebook.tooltip">
|
||||
|
@ -909,9 +891,7 @@
|
|||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetNotebookPage">
|
||||
<div>
|
||||
<t t-call="WidgetFrame"/>
|
||||
</div>
|
||||
<t t-call="WidgetFrame"/>
|
||||
</t>
|
||||
<t t-name="WidgetSeparator">
|
||||
<div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
|
||||
|
@ -1107,11 +1087,9 @@
|
|||
<t t-name="FieldReference">
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
|
||||
<tr>
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_selection #{widget.selection.element_class}">
|
||||
<t t-raw="widget.selection.render()"/>
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_selection oe_form_view_reference_selection">
|
||||
</td>
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_many2one #{widget.m2o.element_class}" nowrap="true" style="display: none">
|
||||
<t t-raw="widget.m2o.render()"/>
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -1128,7 +1106,7 @@
|
|||
</div>
|
||||
</t>
|
||||
<t t-name="FieldBinaryImage">
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img t-att-src='_s + "/web/static/src/img/placeholder.png"' class="oe-binary-image"
|
||||
|
@ -1523,21 +1501,20 @@
|
|||
<td t-if="edited and !options.deletable" class="oe-listview-padding"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="ListView.row.frame" t-extend="WidgetFrame">
|
||||
<t t-jquery="tr">
|
||||
$(document.createElement('t'))
|
||||
.append(this.contents())
|
||||
.attr({
|
||||
't-foreach': this.attr('t-foreach'),
|
||||
't-as': this.attr('t-as')
|
||||
})
|
||||
.replaceAll(this)
|
||||
.after($(document.createElement('td')).append(
|
||||
$(document.createElement('button')).attr({
|
||||
'class': 'oe-edit-row-save', 'type': 'button'})
|
||||
.html(' ')))
|
||||
.unwrap();
|
||||
<t t-name="ListView.row.frame">
|
||||
<t t-foreach="widget.table" t-as="row">
|
||||
<t t-foreach="row" t-as="td">
|
||||
<td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined"
|
||||
t-att-valign="td.table ? 'top' : undefined"
|
||||
t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class} oe-field-cell"
|
||||
>
|
||||
<t t-raw="td.render()"/>
|
||||
</td>
|
||||
</t>
|
||||
</t>
|
||||
<td>
|
||||
<button class="oe-edit-row-save" type="button"> </button>
|
||||
</td>
|
||||
</t>
|
||||
<t t-name="view_editor">
|
||||
<table class="oe_view_editor">
|
||||
|
|
|
@ -10,6 +10,7 @@ class TestDataSetController(unittest2.TestCase):
|
|||
self.read = self.request.session.model().read
|
||||
self.search = self.request.session.model().search
|
||||
|
||||
@unittest2.skip
|
||||
def test_empty_find(self):
|
||||
self.search.return_value = []
|
||||
self.read.return_value = []
|
||||
|
@ -17,6 +18,7 @@ class TestDataSetController(unittest2.TestCase):
|
|||
self.assertFalse(self.dataset.do_search_read(self.request, 'fake.model'))
|
||||
self.read.assert_called_once_with([], False, self.request.context)
|
||||
|
||||
@unittest2.skip
|
||||
def test_regular_find(self):
|
||||
self.search.return_value = [1, 2, 3]
|
||||
|
||||
|
@ -24,6 +26,7 @@ class TestDataSetController(unittest2.TestCase):
|
|||
self.read.assert_called_once_with([1, 2, 3], False,
|
||||
self.request.context)
|
||||
|
||||
@unittest2.skip
|
||||
def test_ids_shortcut(self):
|
||||
self.search.return_value = [1, 2, 3]
|
||||
self.read.return_value = [
|
||||
|
@ -37,6 +40,7 @@ class TestDataSetController(unittest2.TestCase):
|
|||
[{'id': 1}, {'id': 2}, {'id': 3}])
|
||||
self.assertFalse(self.read.called)
|
||||
|
||||
@unittest2.skip
|
||||
def test_get(self):
|
||||
self.read.return_value = [
|
||||
{'id': 1, 'name': 'baz'},
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import mock
|
||||
import unittest2
|
||||
import web.controllers.main
|
||||
import openerpweb.openerpweb
|
||||
|
||||
from ..controllers import main
|
||||
from ..common.session import OpenERPSession
|
||||
|
||||
class Placeholder(object):
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -11,17 +12,16 @@ class Placeholder(object):
|
|||
|
||||
class LoadTest(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
self.menu = web.controllers.main.Menu()
|
||||
self.menu = main.Menu()
|
||||
self.menus_mock = mock.Mock()
|
||||
self.request = Placeholder(
|
||||
session=openerpweb.openerpweb.OpenERPSession(
|
||||
model_factory=lambda _session, _name: self.menus_mock))
|
||||
self.request = Placeholder(session=OpenERPSession())
|
||||
|
||||
def tearDown(self):
|
||||
del self.request
|
||||
del self.menus_mock
|
||||
del self.menu
|
||||
|
||||
@unittest2.skip
|
||||
def test_empty(self):
|
||||
self.menus_mock.search = mock.Mock(return_value=[])
|
||||
self.menus_mock.read = mock.Mock(return_value=[])
|
||||
|
@ -36,6 +36,7 @@ class LoadTest(unittest2.TestCase):
|
|||
root['children'],
|
||||
[])
|
||||
|
||||
@unittest2.skip
|
||||
def test_applications_sort(self):
|
||||
self.menus_mock.search = mock.Mock(return_value=[1, 2, 3])
|
||||
self.menus_mock.read = mock.Mock(return_value=[
|
||||
|
@ -62,6 +63,7 @@ class LoadTest(unittest2.TestCase):
|
|||
'parent_id': False, 'children': []
|
||||
}])
|
||||
|
||||
@unittest2.skip
|
||||
def test_deep(self):
|
||||
self.menus_mock.search = mock.Mock(return_value=[1, 2, 3, 4])
|
||||
self.menus_mock.read = mock.Mock(return_value=[
|
||||
|
@ -100,7 +102,7 @@ class LoadTest(unittest2.TestCase):
|
|||
|
||||
class ActionMungerTest(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
self.menu = web.controllers.main.Menu()
|
||||
self.menu = main.Menu()
|
||||
def test_actual_treeview(self):
|
||||
action = {
|
||||
"views": [[False, "tree"], [False, "form"],
|
||||
|
@ -111,10 +113,11 @@ class ActionMungerTest(unittest2.TestCase):
|
|||
}
|
||||
changed = action.copy()
|
||||
del action['view_type']
|
||||
web.controllers.main.fix_view_modes(changed)
|
||||
main.fix_view_modes(changed)
|
||||
|
||||
self.assertEqual(changed, action)
|
||||
|
||||
@unittest2.skip
|
||||
def test_list_view(self):
|
||||
action = {
|
||||
"views": [[False, "tree"], [False, "form"],
|
||||
|
@ -123,7 +126,7 @@ class ActionMungerTest(unittest2.TestCase):
|
|||
"view_id": False,
|
||||
"view_mode": "tree,form,calendar"
|
||||
}
|
||||
web.controllers.main.fix_view_modes(action)
|
||||
main.fix_view_modes(action)
|
||||
|
||||
self.assertEqual(action, {
|
||||
"views": [[False, "list"], [False, "form"],
|
||||
|
@ -132,6 +135,7 @@ class ActionMungerTest(unittest2.TestCase):
|
|||
"view_mode": "list,form,calendar"
|
||||
})
|
||||
|
||||
@unittest2.skip
|
||||
def test_redundant_views(self):
|
||||
|
||||
action = {
|
||||
|
@ -141,7 +145,7 @@ class ActionMungerTest(unittest2.TestCase):
|
|||
"view_id": False,
|
||||
"view_mode": "tree,form,calendar"
|
||||
}
|
||||
web.controllers.main.fix_view_modes(action)
|
||||
main.fix_view_modes(action)
|
||||
|
||||
self.assertEqual(action, {
|
||||
"views": [[False, "list"], [False, "form"],
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import random
|
||||
import unittest2
|
||||
|
||||
from ..controllers.main import topological_sort
|
||||
|
||||
def sample(population):
|
||||
return random.sample(
|
||||
population,
|
||||
random.randint(0, min(len(population), 5)))
|
||||
|
||||
class TestModulesLoading(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
self.mods = map(str, range(1000))
|
||||
def test_topological_sort(self):
|
||||
random.shuffle(self.mods)
|
||||
modules = [
|
||||
(k, sample(self.mods[:i]))
|
||||
for i, k in enumerate(self.mods)]
|
||||
random.shuffle(modules)
|
||||
ms = dict(modules)
|
||||
|
||||
seen = set()
|
||||
sorted_modules = topological_sort(ms)
|
||||
for module in sorted_modules:
|
||||
deps = ms[module]
|
||||
self.assertGreaterEqual(
|
||||
seen, set(deps),
|
||||
'Module %s (index %d), ' \
|
||||
'missing dependencies %s from loaded modules %s' % (
|
||||
module, sorted_modules.index(module), deps, seen
|
||||
))
|
||||
seen.add(module)
|
|
@ -6,8 +6,7 @@ import unittest2
|
|||
import simplejson
|
||||
|
||||
import web.controllers.main
|
||||
import openerpweb.nonliterals
|
||||
import openerpweb.openerpweb
|
||||
from ..common import nonliterals, session as s
|
||||
|
||||
def field_attrs(fields_view_get, fieldname):
|
||||
(field,) = filter(lambda f: f['attrs'].get('name') == fieldname,
|
||||
|
@ -47,7 +46,7 @@ class DomainsAndContextsTest(unittest2.TestCase):
|
|||
)
|
||||
|
||||
def test_retrieve_nonliteral_domain(self):
|
||||
session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession)
|
||||
session = mock.Mock(spec=s.OpenERPSession)
|
||||
session.domains_store = {}
|
||||
domain_string = ("[('month','=',(datetime.date.today() - "
|
||||
"datetime.timedelta(365/12)).strftime('%%m'))]")
|
||||
|
@ -56,9 +55,9 @@ class DomainsAndContextsTest(unittest2.TestCase):
|
|||
|
||||
self.view.parse_domains_and_contexts(e, session)
|
||||
|
||||
self.assertIsInstance(e.get('domain'), openerpweb.nonliterals.Domain)
|
||||
self.assertIsInstance(e.get('domain'), nonliterals.Domain)
|
||||
self.assertEqual(
|
||||
openerpweb.nonliterals.Domain(
|
||||
nonliterals.Domain(
|
||||
session, key=e.get('domain').key).get_domain_string(),
|
||||
domain_string)
|
||||
|
||||
|
@ -90,7 +89,7 @@ class DomainsAndContextsTest(unittest2.TestCase):
|
|||
)
|
||||
|
||||
def test_retrieve_nonliteral_context(self):
|
||||
session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession)
|
||||
session = mock.Mock(spec=s.OpenERPSession)
|
||||
session.contexts_store = {}
|
||||
context_string = ("{'month': (datetime.date.today() - "
|
||||
"datetime.timedelta(365/12)).strftime('%%m')}")
|
||||
|
@ -99,9 +98,9 @@ class DomainsAndContextsTest(unittest2.TestCase):
|
|||
|
||||
self.view.parse_domains_and_contexts(e, session)
|
||||
|
||||
self.assertIsInstance(e.get('context'), openerpweb.nonliterals.Context)
|
||||
self.assertIsInstance(e.get('context'), nonliterals.Context)
|
||||
self.assertEqual(
|
||||
openerpweb.nonliterals.Context(
|
||||
nonliterals.Context(
|
||||
session, key=e.get('context').key).get_context_string(),
|
||||
context_string)
|
||||
|
||||
|
@ -127,6 +126,8 @@ class AttrsNormalizationTest(unittest2.TestCase):
|
|||
xml.etree.ElementTree.tostring(transformed),
|
||||
xml.etree.ElementTree.tostring(pristine)
|
||||
)
|
||||
|
||||
@unittest2.skip
|
||||
def test_transform_states(self):
|
||||
element = xml.etree.ElementTree.Element(
|
||||
'field', states="open,closed")
|
||||
|
@ -137,6 +138,7 @@ class AttrsNormalizationTest(unittest2.TestCase):
|
|||
simplejson.loads(element.get('attrs')),
|
||||
{'invisible': [['state', 'not in', ['open', 'closed']]]})
|
||||
|
||||
@unittest2.skip
|
||||
def test_transform_invisible(self):
|
||||
element = xml.etree.ElementTree.Element(
|
||||
'field', invisible="context.get('invisible_country', False)")
|
||||
|
@ -149,12 +151,13 @@ class AttrsNormalizationTest(unittest2.TestCase):
|
|||
self.view.normalize_attrs(full_context, {'invisible_country': True})
|
||||
self.assertEqual(full_context.get('invisible'), '1')
|
||||
|
||||
@unittest2.skip
|
||||
def test_transform_invisible_list_column(self):
|
||||
req = mock.Mock()
|
||||
req.context = {'set_editable':True, 'set_visible':True,
|
||||
'gtd_visible':True, 'user_invisible':True}
|
||||
req.session.evaluation_context = \
|
||||
openerpweb.openerpweb.OpenERPSession().evaluation_context
|
||||
s.OpenERPSession().evaluation_context
|
||||
req.session.model('project.task').fields_view_get.return_value = {
|
||||
'arch': '''
|
||||
<tree colors="grey:state in ('cancelled','done');blue:state == 'pending';red:date_deadline and (date_deadline<current_date) and (state in ('draft','pending','open'))" string="Tasks">
|
||||
|
@ -183,13 +186,17 @@ class ListViewTest(unittest2.TestCase):
|
|||
self.view = web.controllers.main.ListView()
|
||||
self.request = mock.Mock()
|
||||
self.request.context = {'set_editable': True}
|
||||
|
||||
@unittest2.skip
|
||||
def test_no_editable_editable_context(self):
|
||||
self.request.session.model('fake').fields_view_get.return_value = \
|
||||
{'arch': '<tree><field name="foo"/></tree>'}
|
||||
view = self.view.fields_view_get(self.request, 'fake', False)
|
||||
view = self.view.fields_view_get(self.request, 'fake', False, False)
|
||||
|
||||
self.assertEqual(view['arch']['attrs']['editable'],
|
||||
'bottom')
|
||||
|
||||
@unittest2.skip
|
||||
def test_editable_top_editable_context(self):
|
||||
self.request.session.model('fake').fields_view_get.return_value = \
|
||||
{'arch': '<tree editable="top"><field name="foo"/></tree>'}
|
||||
|
@ -198,6 +205,7 @@ class ListViewTest(unittest2.TestCase):
|
|||
self.assertEqual(view['arch']['attrs']['editable'],
|
||||
'top')
|
||||
|
||||
@unittest2.skip
|
||||
def test_editable_bottom_editable_context(self):
|
||||
self.request.session.model('fake').fields_view_get.return_value = \
|
||||
{'arch': '<tree editable="bottom"><field name="foo"/></tree>'}
|
||||
|
|
|
@ -19,5 +19,5 @@
|
|||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
'active': True
|
||||
'auto_install': True
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# Czech translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-03-04 12:08+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Czech <cs@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-05 04:53+0000\n"
|
||||
"X-Generator: Launchpad (build 14900)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
msgid "Responsible"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
msgid "Navigator"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr ""
|
|
@ -8,15 +8,16 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 02:18-0600\n"
|
||||
"Last-Translator: Carlos Vásquez - CLEARCORP <carlos.vasquez@clearcorp.co.cr>\n"
|
||||
"PO-Revision-Date: 2012-02-16 21:34+0000\n"
|
||||
"Last-Translator: Carlos Vásquez (CLEARCORP) "
|
||||
"<carlos.vasquez@clearcorp.co.cr>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-07 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 14747)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
|
@ -38,4 +39,3 @@ msgstr "Navegador"
|
|||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Finnish translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-03-19 12:03+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-20 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14969)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "kalenteri"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
#: addons/web_calendar/static/src/js/calendar.js:467
|
||||
msgid "Responsible"
|
||||
msgstr "Vastuuhenkilö"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
#: addons/web_calendar/static/src/js/calendar.js:505
|
||||
msgid "Navigator"
|
||||
msgstr "Navigaattori"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr " "
|
|
@ -8,29 +8,29 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2011-10-08 13:39+0000\n"
|
||||
"Last-Translator: Nicola Riolini - Micronaet <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-16 21:55+0000\n"
|
||||
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
|
||||
"Language-Team: Italian <it@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-07 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 14747)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr ""
|
||||
msgstr "Calendario"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
msgid "Responsible"
|
||||
msgstr ""
|
||||
msgstr "Responsabile"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
msgid "Navigator"
|
||||
msgstr ""
|
||||
msgstr "Navigatore"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# Japanese translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-02-22 02:18+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-23 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 14855)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "カレンダー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
msgid "Responsible"
|
||||
msgstr "責任担当"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
msgid "Navigator"
|
||||
msgstr "ナビゲータ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr " "
|
|
@ -0,0 +1,41 @@
|
|||
# Georgian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-03-15 18:25+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Georgian <ka@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-16 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 14951)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "კალენდარი"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
#: addons/web_calendar/static/src/js/calendar.js:467
|
||||
msgid "Responsible"
|
||||
msgstr "პასუხისმგებელი"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
#: addons/web_calendar/static/src/js/calendar.js:505
|
||||
msgid "Navigator"
|
||||
msgstr "ნავიგატორი"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr ""
|
|
@ -8,19 +8,19 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-02-15 14:37+0000\n"
|
||||
"PO-Revision-Date: 2012-02-16 14:02+0000\n"
|
||||
"Last-Translator: Erwin <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "Kalender"
|
||||
msgstr "Agenda"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Romanian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-03-10 13:17+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-11 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 14914)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "Calendar"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:466
|
||||
#: addons/web_calendar/static/src/js/calendar.js:467
|
||||
msgid "Responsible"
|
||||
msgstr "Responsabil"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:504
|
||||
#: addons/web_calendar/static/src/js/calendar.js:505
|
||||
msgid "Navigator"
|
||||
msgstr "Navigator"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr " "
|
|
@ -42,7 +42,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
|
|||
this._super();
|
||||
return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': true}, this.on_loaded);
|
||||
},
|
||||
stop: function() {
|
||||
destroy: function() {
|
||||
scheduler.clearAll();
|
||||
this._super();
|
||||
},
|
||||
|
@ -66,6 +66,8 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
|
|||
|
||||
this.day_length = this.fields_view.arch.attrs.day_length || 8;
|
||||
this.color_field = this.fields_view.arch.attrs.color;
|
||||
this.color_string = this.fields_view.fields[this.color_field] ?
|
||||
this.fields_view.fields[this.color_field].string : _t("Filter");
|
||||
|
||||
if (this.color_field && this.selected_filters.length === 0) {
|
||||
var default_filter;
|
||||
|
@ -463,8 +465,9 @@ openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({
|
|||
});
|
||||
|
||||
openerp.web_calendar.SidebarResponsible = openerp.web.OldWidget.extend({
|
||||
// TODO: fme: in trunk, rename this class to SidebarFilter
|
||||
init: function(parent, view) {
|
||||
var $section = parent.add_section(_t('Responsible'), 'responsible');
|
||||
var $section = parent.add_section(view.color_string, 'responsible');
|
||||
this.$div = $('<div></div>');
|
||||
$section.append(this.$div);
|
||||
this._super(parent, $section.attr('id'));
|
||||
|
|
|
@ -14,5 +14,5 @@
|
|||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
'active': True
|
||||
'auto_install': True
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
# Czech translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-03-04 12:09+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Czech <cs@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-05 04:53+0000\n"
|
||||
"X-Generator: Launchpad (build 14900)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62
|
||||
msgid "progress:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr ""
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-13 17:56+0000\n"
|
||||
"Last-Translator: Amós Oviedo <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-22 10:34+0000\n"
|
||||
"Last-Translator: Jorge L Tupac-Yupanqui <Unknown>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-23 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 14855)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -36,12 +36,12 @@ msgstr "Sin categoría"
|
|||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Ejecutar tarea \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Marcar esta tarea como terminada"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
|
|
|
@ -7,16 +7,16 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2012-02-07 10:13+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 02:25-0600\n"
|
||||
"Last-Translator: Carlos Vásquez - CLEARCORP <carlos.vasquez@clearcorp.co.cr>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-16 22:16+0000\n"
|
||||
"Last-Translator: Freddy Gonzalez <freddy.gonzalez@clearcorp.co.cr>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-08 06:39+0000\n"
|
||||
"X-Generator: Launchpad (build 14747)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -33,6 +33,17 @@ msgstr "¿Está seguro/a que desea eliminar este elemento?"
|
|||
msgid "Uncategorized"
|
||||
msgstr "Sin categoría"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr "Ejecutar tarea \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr "Marcar esta tarea como terminada"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
|
@ -75,8 +86,12 @@ msgstr "progreso:"
|
|||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid "Click on the functionalites listed below to launch them and configure your system"
|
||||
msgstr "Haga clic en las funcionalidades enumeradas abajo para lanzarlas y configurar su sistema"
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
"Haga clic en las funcionalidades enumeradas abajo para lanzarlas y "
|
||||
"configurar su sistema"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
|
@ -97,4 +112,3 @@ msgstr "Esta dirección"
|
|||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr "Su usuario:"
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
# Finnish translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-03-19 12:00+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-20 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14969)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr "Muokkaa näkymää"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr "Oletko varma että haluat poistaa tämän osan ?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr "Luokittelemattomat"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr "Suorita tehtävä \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr "Merkitse tämä tehtävä valmiiksi"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr "Palauta asettelu.."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr "Palauta"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr "Muuta asettelu.."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr "Muuta asettelu"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr "Luo"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr "Valitse työpöydän asetttelu"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62
|
||||
msgid "progress:"
|
||||
msgstr "edistyminen:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
"Klikkaa allalistattuja toimintoja käynnistääksesi ne ja määritelläksesi "
|
||||
"järjestelmäsi"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr "Tervetuloa OpenERP järjestelmään"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr "Muista luoda kirjainmerkki"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr "Tämä osoite"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr "Käyttäjätunnuksesi:"
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-13 23:18+0000\n"
|
||||
"Last-Translator: Amós Oviedo <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-16 09:13+0000\n"
|
||||
"Last-Translator: Vicente <jviares@gmail.com>\n"
|
||||
"Language-Team: Galician <gl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -36,12 +36,12 @@ msgstr "Sen categorizar"
|
|||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Executar Tarefa \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Marcar esta tarefa como feita"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 08:52+0000\n"
|
||||
"Last-Translator: Marijan Rajic <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-22 10:13+0000\n"
|
||||
"Last-Translator: Goran Kliska <gkliska@gmail.com>\n"
|
||||
"Language-Team: Croatian <hr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-23 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 14855)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -36,12 +36,12 @@ msgstr "Nekategorizirano"
|
|||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Izvrši zadatak \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Označi ovaj zadatak kao obavljen"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
|
|
|
@ -8,45 +8,45 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2011-10-07 09:00+0000\n"
|
||||
"PO-Revision-Date: 2012-02-16 21:54+0000\n"
|
||||
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
|
||||
"Language-Team: Italian <it@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr ""
|
||||
msgstr "Modifica Layour"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr ""
|
||||
msgstr "Sicuro di voler cancellare questo elemento?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr ""
|
||||
msgstr "Non categorizzato"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Esegui task \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Marca questa attività come completata"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr ""
|
||||
msgstr "Reimposta Layout.."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
|
@ -56,22 +56,22 @@ msgstr "Ripristina"
|
|||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr ""
|
||||
msgstr "Cambia Layout.."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr ""
|
||||
msgstr "Cambia Layout"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr ""
|
||||
msgstr " "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
msgstr "Crea"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
|
@ -89,23 +89,25 @@ msgid ""
|
|||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
"Clicca sulla lista di funzionalità seguenti per eseguirle e configurare il "
|
||||
"sistema"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr ""
|
||||
msgstr "Benvenuto su OpenERP"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr ""
|
||||
msgstr "Salva nei bookmark"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr ""
|
||||
msgstr "Questo url"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr ""
|
||||
msgstr "Il tuo login:"
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
# Japanese translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-03-06 06:40+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-07 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 14907)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr "レイアウトを編集"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr "この項目を取り除きますか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr "未分類"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr "タスク \"%s\" を実行"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr "このタスクを完了"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr "レイアウトをリセット"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr "リセット"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr "レイアウトを変更…"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr "レイアウトを変更"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62
|
||||
msgid "progress:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr ""
|
|
@ -0,0 +1,112 @@
|
|||
# Georgian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-03-15 18:30+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Georgian <ka@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-16 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 14951)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr "განლაგების შეცვლა"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr "დარწმუნებული ხართ რომ გსურთ ამ კომპონენტის წაშლა?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr "კატეგორიის გარეშე"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr "შეასრულე ამოცანა \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr "მონიშნე ეს ამოცანა როგორც დასრულებული"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr "საწყისი განლაგება"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr "ხელახალი კონფიგურაცია"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr "განლაგების შეცვლა.."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr "განლაგების შეცვლა"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr "შექმნა"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr "აირჩიეთ საინფორმაციო დაფის განლაგება"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62
|
||||
msgid "progress:"
|
||||
msgstr "პროგრესი:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
"დააწკაპუნეთ ქვემოთ ჩამოთვლილ ფუნქციონალზე რათა გაააქტიუროთ და დააკონფიგურიროთ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr "მოგესალმებით!"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr "ჩაინიშნე ფავორიტებში"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr "ეს URL"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr "თქვენი მოხმარებელი:"
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-15 14:38+0000\n"
|
||||
"PO-Revision-Date: 2012-03-19 08:25+0000\n"
|
||||
"Last-Translator: Erwin <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-20 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14969)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -100,14 +100,14 @@ msgstr "Welkom bij OpenERP"
|
|||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr "Bookmark maken"
|
||||
msgstr "Vergeet niet een bladwijzer aan te maken van"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr "Deze URL"
|
||||
msgstr "Deze pagina"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr "U login:"
|
||||
msgstr "Uw login:"
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-16 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
# Romanian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-03-10 13:19+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-11 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 14914)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
msgid "Edit Layout"
|
||||
msgstr "Editare aspect"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:109
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:316
|
||||
msgid "Uncategorized"
|
||||
msgstr "Fără categorie"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr "Marchează acestă sarcină ca realizată"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr "Restează aspect ..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr "Resetare"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr "Schimbare aspect..."
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr "Modificare aspect"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr "Crează"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62
|
||||
msgid "progress:"
|
||||
msgstr "progres:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67
|
||||
msgid ""
|
||||
"Click on the functionalites listed below to launch them and configure your "
|
||||
"system"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110
|
||||
msgid "Welcome to OpenERP"
|
||||
msgstr "Bun venit in OpenERP"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118
|
||||
msgid "Remember to bookmark"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119
|
||||
msgid "This url"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121
|
||||
msgid "Your login:"
|
||||
msgstr ""
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 07:12+0000\n"
|
||||
"PO-Revision-Date: 2012-02-17 07:37+0000\n"
|
||||
"Last-Translator: Aleksei Motsik <Unknown>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-18 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -36,12 +36,12 @@ msgstr "Без категории"
|
|||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Выполнить задачу \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Отметить завершенной"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
|
||||
"PO-Revision-Date: 2012-02-09 20:10+0000\n"
|
||||
"PO-Revision-Date: 2012-02-24 11:28+0000\n"
|
||||
"Last-Translator: Ahmet Altınışık <Unknown>\n"
|
||||
"Language-Team: Turkish <tr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 14781)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-25 05:30+0000\n"
|
||||
"X-Generator: Launchpad (build 14860)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:63
|
||||
|
@ -36,12 +36,12 @@ msgstr "Sınıflandırılmamış"
|
|||
#: addons/web_dashboard/static/src/js/dashboard.js:324
|
||||
#, python-format
|
||||
msgid "Execute task \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Görevi Çalıştır \"%s\""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:325
|
||||
msgid "Mark this task as done"
|
||||
msgstr ""
|
||||
msgstr "Bu görevi yapıldı olarak işaretle"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
|
|
|
@ -10,7 +10,7 @@ if (!openerp.web_dashboard) {
|
|||
openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = 'DashBoard';
|
||||
this.form_template = 'DashBoard';
|
||||
this.actions_attrs = {};
|
||||
this.action_managers = [];
|
||||
},
|
||||
|
@ -31,18 +31,16 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
this.$element.delegate('.oe-dashboard-column .oe-dashboard-fold', 'click', this.on_fold_action);
|
||||
this.$element.delegate('.oe-dashboard-column .ui-icon-closethick', 'click', this.on_close_action);
|
||||
|
||||
this.actions_attrs = {};
|
||||
// Init actions
|
||||
_.each(this.node.children, function(column, column_index) {
|
||||
_.each(column.children, function(action, action_index) {
|
||||
delete(action.attrs.width);
|
||||
delete(action.attrs.height);
|
||||
delete(action.attrs.colspan);
|
||||
self.actions_attrs[action.attrs.name] = action.attrs;
|
||||
self.rpc('/web/action/load', {
|
||||
action_id: parseInt(action.attrs.name, 10)
|
||||
}, function(result) {
|
||||
self.on_load_action(result, column_index + '_' + action_index);
|
||||
self.on_load_action(result, column_index + '_' + action_index, action.attrs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -58,7 +56,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
var qdict = {
|
||||
current_layout : this.$element.find('.oe-dashboard').attr('data-layout')
|
||||
};
|
||||
var $dialog = $('<div>').dialog({
|
||||
var $dialog = openerp.web.dialog($('<div>'), {
|
||||
modal: true,
|
||||
title: _t("Edit Layout"),
|
||||
width: 'auto',
|
||||
|
@ -97,9 +95,9 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
$action = $e.parents('.oe-dashboard-action:first'),
|
||||
id = parseInt($action.attr('data-id'), 10);
|
||||
if ($e.is('.ui-icon-minusthick')) {
|
||||
this.actions_attrs[id].fold = '1';
|
||||
$action.data('action_attrs').fold = '1';
|
||||
} else {
|
||||
delete(this.actions_attrs[id].fold);
|
||||
delete($action.data('action_attrs').fold);
|
||||
}
|
||||
$e.toggleClass('ui-icon-minusthick ui-icon-plusthick');
|
||||
$action.find('.oe-dashboard-action-content').toggle();
|
||||
|
@ -122,7 +120,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
var actions = [];
|
||||
$(this).find('.oe-dashboard-action').each(function() {
|
||||
var action_id = $(this).attr('data-id'),
|
||||
new_attrs = _.clone(self.actions_attrs[action_id]);
|
||||
new_attrs = _.clone($(this).data('action_attrs'));
|
||||
if (new_attrs.domain) {
|
||||
new_attrs.domain = new_attrs.domain_string;
|
||||
delete(new_attrs.domain_string);
|
||||
|
@ -143,19 +141,25 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
self.$element.find('.oe-dashboard-link-reset').show();
|
||||
});
|
||||
},
|
||||
on_load_action: function(result, index) {
|
||||
on_load_action: function(result, index, action_attrs) {
|
||||
var self = this,
|
||||
action = result.result,
|
||||
action_attrs = this.actions_attrs[action.id],
|
||||
view_mode = action_attrs.view_mode;
|
||||
|
||||
if (action_attrs.context) {
|
||||
action.context = _.extend((action.context || {}), action_attrs.context);
|
||||
}
|
||||
if (action_attrs.domain) {
|
||||
action.domain = action.domain || [];
|
||||
action.domain.unshift.apply(action.domain, action_attrs.domain);
|
||||
if (action_attrs.context && action_attrs.context['dashboard_merge_domains_contexts'] === false) {
|
||||
// TODO: replace this 6.1 workaround by attribute on <action/>
|
||||
action.context = action_attrs.context || {};
|
||||
action.domain = action_attrs.domain || [];
|
||||
} else {
|
||||
if (action_attrs.context) {
|
||||
action.context = _.extend((action.context || {}), action_attrs.context);
|
||||
}
|
||||
if (action_attrs.domain) {
|
||||
action.domain = action.domain || [];
|
||||
action.domain.unshift.apply(action.domain, action_attrs.domain);
|
||||
}
|
||||
}
|
||||
|
||||
var action_orig = _.extend({ flags : {} }, action);
|
||||
|
||||
if (view_mode && view_mode != action.view_mode) {
|
||||
|
@ -187,6 +191,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
var am = new openerp.web.ActionManager(this),
|
||||
// FIXME: ideally the dashboard view shall be refactored like kanban.
|
||||
$action = $('#' + this.view.element_id + '_action_' + index);
|
||||
$action.parent().data('action_attrs', action_attrs);
|
||||
this.action_managers.push(am);
|
||||
am.appendTo($action);
|
||||
am.do_action(action);
|
||||
|
@ -225,7 +230,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
});
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
renderElement: function() {
|
||||
// We should start with three columns available
|
||||
for (var i = this.node.children.length; i < 3; i++) {
|
||||
this.node.children.push({
|
||||
|
@ -234,17 +239,18 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
|
|||
children: []
|
||||
});
|
||||
}
|
||||
return QWeb.render(this.template, this);
|
||||
var rendered = QWeb.render(this.form_template, this);
|
||||
this.$element.html(rendered);
|
||||
},
|
||||
do_reload: function() {
|
||||
var view_manager = this.view.widget_parent,
|
||||
action_manager = view_manager.widget_parent;
|
||||
this.view.stop();
|
||||
var view_manager = this.view.getParent(),
|
||||
action_manager = view_manager.getParent();
|
||||
this.view.destroy();
|
||||
action_manager.do_action(view_manager.action);
|
||||
}
|
||||
});
|
||||
openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({
|
||||
render: function() {
|
||||
renderElement: function() {
|
||||
if (this.node.tag == 'hpaned') {
|
||||
this.node.attrs.style = '2-1';
|
||||
} else if (this.node.tag == 'vpaned') {
|
||||
|
@ -263,7 +269,7 @@ openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({
|
|||
}
|
||||
}
|
||||
});
|
||||
return this._super(this, arguments);
|
||||
this._super(this);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -340,7 +346,7 @@ openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({
|
|||
});
|
||||
})
|
||||
.delegate('li:not(.oe-done)', 'click', function () {
|
||||
self.widget_parent.widget_parent.widget_parent.do_execute_action({
|
||||
self.getParent().getParent().getParent().do_execute_action({
|
||||
type: 'object',
|
||||
name: 'action_launch'
|
||||
}, self.dataset,
|
||||
|
@ -405,7 +411,6 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({
|
|||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
openerp.webclient.menu.do_hide_secondary();
|
||||
var domain = [['application','=',true], ['state','=','installed'], ['name', '!=', 'base']];
|
||||
var ds = new openerp.web.DataSetSearch(this, 'ir.module.module',{},domain);
|
||||
ds.read_slice(['id']).then(function(result) {
|
||||
|
@ -462,8 +467,8 @@ openerp.web_dashboard.ApplicationInstaller = openerp.web.OldWidget.extend({
|
|||
});
|
||||
return r;
|
||||
},
|
||||
stop: function() {
|
||||
this.action_manager.stop();
|
||||
destroy: function() {
|
||||
this.action_manager.destroy();
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
"version" : "2.0",
|
||||
"depends" : ["web"],
|
||||
"js": [
|
||||
'static/lib/js/raphael-min.js',
|
||||
'static/lib/js/dracula_graffle.js',
|
||||
'static/lib/js/dracula_graph.js',
|
||||
'static/lib/js/dracula_algorithms.js',
|
||||
'static/src/js/diagram.js'
|
||||
'static/lib/js/raphael.js',
|
||||
'static/lib/js/jquery.mousewheel.js',
|
||||
'static/src/js/vec2.js',
|
||||
'static/src/js/graph.js',
|
||||
'static/src/js/diagram.js',
|
||||
],
|
||||
'css' : [
|
||||
"static/src/css/base_diagram.css",
|
||||
|
@ -17,5 +17,5 @@
|
|||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
'active': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
|
|
@ -115,8 +115,8 @@ class DiagramView(View):
|
|||
for i, fld in enumerate(visible_node_fields):
|
||||
n['options'][node_fields_string[i]] = act[fld]
|
||||
|
||||
id_model = req.session.model(model).read([id],['name'], req.session.context)[0]['name']
|
||||
_id, name = req.session.model(model).name_get([id], req.session.context)[0]
|
||||
return dict(nodes=nodes,
|
||||
conn=connectors,
|
||||
id_model=id_model,
|
||||
name=name,
|
||||
parent_field=graphs['node_parent_field'])
|
||||
|
|
|
@ -8,15 +8,16 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-02-08 02:25-0600\n"
|
||||
"Last-Translator: Carlos Vásquez - CLEARCORP <carlos.vasquez@clearcorp.co.cr>\n"
|
||||
"PO-Revision-Date: 2012-02-16 21:35+0000\n"
|
||||
"Last-Translator: Carlos Vásquez (CLEARCORP) "
|
||||
"<carlos.vasquez@clearcorp.co.cr>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-07 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 14747)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:11
|
||||
|
@ -41,10 +42,9 @@ msgstr "Crear:"
|
|||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:231
|
||||
msgid "Open: "
|
||||
msgstr "Abrir:"
|
||||
msgstr "Abrir: "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/xml/base_diagram.xml:5
|
||||
msgid "New Node"
|
||||
msgstr "Nuevo Nodo"
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# Finnish translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2012-03-19 11:59+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-20 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14969)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:11
|
||||
msgid "Diagram"
|
||||
msgstr "Kaavio"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:208
|
||||
#: addons/web_diagram/static/src/js/diagram.js:224
|
||||
#: addons/web_diagram/static/src/js/diagram.js:257
|
||||
msgid "Activity"
|
||||
msgstr "Aktiviteetti"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:208
|
||||
#: addons/web_diagram/static/src/js/diagram.js:289
|
||||
#: addons/web_diagram/static/src/js/diagram.js:308
|
||||
msgid "Transition"
|
||||
msgstr "Siirtyminen"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:214
|
||||
#: addons/web_diagram/static/src/js/diagram.js:262
|
||||
#: addons/web_diagram/static/src/js/diagram.js:314
|
||||
msgid "Create:"
|
||||
msgstr "Luo:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:231
|
||||
#: addons/web_diagram/static/src/js/diagram.js:232
|
||||
#: addons/web_diagram/static/src/js/diagram.js:296
|
||||
msgid "Open: "
|
||||
msgstr "Avaa: "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/xml/base_diagram.xml:5
|
||||
#: addons/web_diagram/static/src/xml/base_diagram.xml:6
|
||||
msgid "New Node"
|
||||
msgstr "Uusi Noodi"
|
|
@ -8,39 +8,39 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-06 17:33+0100\n"
|
||||
"PO-Revision-Date: 2011-10-08 13:41+0000\n"
|
||||
"Last-Translator: Nicola Riolini - Micronaet <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-02-16 21:55+0000\n"
|
||||
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
|
||||
"Language-Team: Italian <it@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-07 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 14747)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-17 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:11
|
||||
msgid "Diagram"
|
||||
msgstr ""
|
||||
msgstr "Diagramma"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:208
|
||||
msgid "Activity"
|
||||
msgstr ""
|
||||
msgstr "Attività"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:208
|
||||
msgid "Transition"
|
||||
msgstr ""
|
||||
msgstr "Transizione"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:214
|
||||
msgid "Create:"
|
||||
msgstr ""
|
||||
msgstr "Crea:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:231
|
||||
msgid "Open: "
|
||||
msgstr ""
|
||||
msgstr "Apri: "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/xml/base_diagram.xml:5
|
||||
|
|