[MERGE] from trunk, fix/adapt some tests

bzr revid: xmo@openerp.com-20121116100840-yyr9b4oru3zdftld
This commit is contained in:
Xavier Morel 2012-11-16 11:08:40 +01:00
commit 0d85c24ae3
529 changed files with 36615 additions and 22525 deletions

View File

@ -1,6 +1,7 @@
{
'name': 'Web',
'category': 'Hidden',
'version': '7.0.1.0',
'description':
"""
OpenERP Web core module.
@ -17,7 +18,7 @@ This module provides the core of the OpenERP Web Client.
"static/lib/datejs/parser.js",
"static/lib/datejs/sugarpak.js",
"static/lib/datejs/extras.js",
"static/lib/jquery/jquery-1.7.2.js",
"static/lib/jquery/jquery-1.8.2.js",
"static/lib/jquery.MD5/jquery.md5.js",
"static/lib/jquery.form/jquery.form.js",
"static/lib/jquery.validate/jquery.validate.js",
@ -25,7 +26,7 @@ This module provides the core of the OpenERP Web Client.
"static/lib/spinjs/spin.js",
"static/lib/jquery.autosize/jquery.autosize.js",
"static/lib/jquery.blockUI/jquery.blockUI.js",
"static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js",
"static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js",
"static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js",
"static/lib/jquery.ui.notify/js/jquery.notify.js",
"static/lib/jquery.deferred-queue/jquery.deferred-queue.js",
@ -38,7 +39,7 @@ This module provides the core of the OpenERP Web Client.
"static/lib/underscore/underscore.string.js",
"static/lib/backbone/backbone.js",
"static/lib/cleditor/jquery.cleditor.js",
"static/lib/py.js/lib/py.js",
"static/lib/py.js/lib/py.js",
"static/src/js/boot.js",
"static/src/js/testing.js",
"static/src/js/corelib.js",
@ -56,7 +57,7 @@ This module provides the core of the OpenERP Web Client.
"static/src/js/view_tree.js",
],
'css' : [
"static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css",
"static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css",
"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",
@ -79,6 +80,9 @@ This module provides the core of the OpenERP Web Client.
"static/test/evals.js",
"static/test/search.js",
"static/test/Widget.js",
"static/test/list.js"
"static/test/list-editable.js"
],
'bootstrap': True,
'twitter': False,
}

View File

@ -81,57 +81,6 @@ def rjsmin(script):
).strip()
return result
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)
def db_list(req):
dbs = []
proxy = req.session.proxy("db")
@ -142,6 +91,17 @@ def db_list(req):
dbs = [i for i in dbs if re.match(r, i)]
return dbs
def db_monodb(req):
# if only one db is listed returns it else return False
try:
dbs = db_list(req)
if len(dbs) == 1:
return dbs[0]
except xmlrpclib.Fault:
# ignore access denied
pass
return False
def module_topological_sort(modules):
""" Return a list of module names sorted so that their dependencies of the
modules are listed before the module itself
@ -231,22 +191,14 @@ def module_installed_bypass_session(dbname):
def module_boot(req):
server_wide_modules = openerp.conf.server_wide_modules or ['web']
return [m for m in server_wide_modules if m in openerpweb.addons_manifest]
# TODO the following will be enabled once we separate the module code and translation loading
serverside = []
dbside = []
for i in server_wide_modules:
if i in openerpweb.addons_manifest:
serverside.append(i)
# if only one db load every module at boot
dbs = []
try:
dbs = db_list(req)
except xmlrpclib.Fault:
# ignore access denied
pass
if len(dbs) == 1:
dbside = module_installed_bypass_session(dbs[0])
monodb = db_monodb(req)
if monodb:
dbside = module_installed_bypass_session(monodb)
dbside = [i for i in dbside if i not in serverside]
addons = serverside + dbside
return addons
@ -311,6 +263,10 @@ def concat_js(file_list):
content = rjsmin(content)
return content, checksum
def fs2web(path):
"""convert FS path into web path"""
return '/'.join(path.split(os.path.sep))
def manifest_glob(req, addons, key):
if addons is None:
addons = module_boot(req)
@ -326,7 +282,7 @@ def manifest_glob(req, addons, key):
globlist = manifest.get(key, [])
for pattern in globlist:
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
r.append((path, path[len(addons_path):]))
r.append((path, fs2web(path[len(addons_path):])))
return r
def manifest_list(req, mods, extension):
@ -549,7 +505,7 @@ def _local_web_translations(trans_file):
messages.append({'id': x.id, 'string': x.string})
return messages
def from_elementtree(el, preserve_whitespaces=False):
def xml2json_from_elementtree(el, preserve_whitespaces=False):
""" xml2json-direct
Simple and straightforward XML-to-JSON converter in Python
New BSD Licensed
@ -569,12 +525,25 @@ def from_elementtree(el, preserve_whitespaces=False):
if el.text and (preserve_whitespaces or el.text.strip() != ''):
kids.append(el.text)
for kid in el:
kids.append(from_elementtree(kid, preserve_whitespaces))
kids.append(xml2json_from_elementtree(kid, preserve_whitespaces))
if kid.tail and (preserve_whitespaces or kid.tail.strip() != ''):
kids.append(kid.tail)
res["children"] = kids
return res
def content_disposition(filename, req):
filename = filename.encode('utf8')
escaped = urllib2.quote(filename)
browser = req.httprequest.user_agent.browser
version = int((req.httprequest.user_agent.version or '0').split('.')[0])
if browser == 'msie' and version < 9:
return "attachment; filename=%s" % escaped
elif browser == 'safari':
return "attachment; filename=%s" % filename
else:
return "attachment; filename*=UTF-8''%s" % escaped
#----------------------------------------------------------
# OpenERP Web web Controllers
#----------------------------------------------------------
@ -598,8 +567,7 @@ html_template = """<!DOCTYPE html>
</head>
<body>
<!--[if lte IE 8]>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>
var test = function() {
CFInstall.check({
@ -673,8 +641,7 @@ class WebClient(openerpweb.Controller):
data = fp.read().decode('utf-8')
path = file_map[f]
# convert FS path into web path
web_dir = '/'.join(os.path.dirname(path).split(os.path.sep))
web_dir = os.path.dirname(path)
data = re.sub(
rx_import,
@ -727,21 +694,20 @@ class WebClient(openerpweb.Controller):
until we have established a valid session. This is meant only
for translating the login page and db management chrome, using
the browser's language. """
lang = req.httprequest.accept_languages.best or 'en'
# For performance reasons we only load a single translation, so for
# sub-languages (that should only be partially translated) we load the
# main language PO instead - that should be enough for the login screen.
if '-' in lang: # RFC2616 uses '-' separators for sublanguages
lang = lang.split('-')[0]
lang = req.lang.split('_')[0]
translations_per_module = {}
for addon_name in mods:
addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po")
if not os.path.exists(f_name):
continue
translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)}
if openerpweb.addons_manifest[addon_name].get('bootstrap'):
addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po")
if not os.path.exists(f_name):
continue
translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)}
return {"modules": translations_per_module,
"lang_parameters": None}
@ -805,15 +771,20 @@ class Database(openerpweb.Controller):
@openerpweb.jsonrequest
def create(self, req, fields):
params = dict(map(operator.itemgetter('name', 'value'), fields))
create_attrs = (
return req.session.proxy("db").create_database(
params['super_admin_pwd'],
params['db_name'],
bool(params.get('demo_data')),
params['db_lang'],
params['create_admin_pwd']
)
params['create_admin_pwd'])
return req.session.proxy("db").create_database(*create_attrs)
@openerpweb.jsonrequest
def duplicate(self, req, fields):
params = dict(map(operator.itemgetter('name', 'value'), fields))
return req.session.proxy("db").duplicate_database(
params['super_admin_pwd'],
params['db_original_name'],
params['db_name'])
@openerpweb.jsonrequest
def duplicate(self, req, fields):
@ -851,7 +822,7 @@ class Database(openerpweb.Controller):
}
return req.make_response(db_dump,
[('Content-Type', 'application/octet-stream; charset=binary'),
('Content-Disposition', 'attachment; filename="' + filename + '"')],
('Content-Disposition', content_disposition(filename, req))],
{'fileToken': int(token)}
)
except xmlrpclib.Fault, e:
@ -1291,17 +1262,6 @@ class DataSet(openerpweb.Controller):
m.write(id, { field: i + offset })
return True
class DataGroup(openerpweb.Controller):
_cp_path = "/web/group"
@openerpweb.jsonrequest
def read(self, req, model, fields, group_by_fields, domain=None, sort=None):
Model = req.session.model(model)
context, domain = eval_context_and_domain(req.session, req.context, domain)
return Model.read_group(
domain or [], fields, group_by_fields, 0, False,
dict(context, group_by=group_by_fields), sort or False)
class View(openerpweb.Controller):
_cp_path = "/web/view"
@ -1335,7 +1295,7 @@ class View(openerpweb.Controller):
xml = self.transform_view(arch, session, evaluation_context)
else:
xml = ElementTree.fromstring(arch)
fvg['arch'] = from_elementtree(xml, preserve_whitespaces)
fvg['arch'] = xml2json_from_elementtree(xml, preserve_whitespaces)
if 'id' in fvg['fields']:
# Special case for id's
@ -1509,11 +1469,24 @@ class Binary(openerpweb.Controller):
try:
if not id:
res = Model.default_get([field], context).get(field)
image_data = base64.b64decode(res)
image_base64 = res
else:
res = Model.read([id], [last_update, field], context)[0]
retag = hashlib.md5(res.get(last_update)).hexdigest()
image_data = base64.b64decode(res.get(field))
image_base64 = res.get(field)
if kw.get('resize'):
resize = kw.get('resize').split(',');
if len(resize) == 2 and int(resize[0]) and int(resize[1]):
width = int(resize[0])
height = int(resize[1])
# resize maximum 500*500
if width > 500: width = 500
if height > 500: height = 500
image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
image_data = base64.b64decode(image_base64)
except (TypeError, xmlrpclib.Fault):
image_data = self.placeholder(req)
headers.append(('ETag', retag))
@ -1527,17 +1500,6 @@ class Binary(openerpweb.Controller):
def placeholder(self, req):
addons_path = openerpweb.addons_manifest['web']['addons_path']
return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
def content_disposition(self, filename, req):
filename = filename.encode('utf8')
escaped = urllib2.quote(filename)
browser = req.httprequest.user_agent.browser
version = int((req.httprequest.user_agent.version or '0').split('.')[0])
if browser == 'msie' and version < 9:
return "attachment; filename=%s" % escaped
elif browser == 'safari':
return "attachment; filename=%s" % filename
else:
return "attachment; filename*=UTF-8''%s" % escaped
@openerpweb.httprequest
def saveas(self, req, model, field, id=None, filename_field=None, **kw):
@ -1573,7 +1535,7 @@ class Binary(openerpweb.Controller):
filename = res.get(filename_field, '') or filename
return req.make_response(filecontent,
[('Content-Type', 'application/octet-stream'),
('Content-Disposition', self.content_disposition(filename, req))])
('Content-Disposition', content_disposition(filename, req))])
@openerpweb.httprequest
def saveas_ajax(self, req, data, token):
@ -1603,7 +1565,7 @@ class Binary(openerpweb.Controller):
filename = res.get(filename_field, '') or filename
return req.make_response(filecontent,
headers=[('Content-Type', 'application/octet-stream'),
('Content-Disposition', self.content_disposition(filename, req))],
('Content-Disposition', content_disposition(filename, req))],
cookies={'fileToken': int(token)})
@openerpweb.httprequest
@ -1868,7 +1830,8 @@ class Export(View):
return req.make_response(self.from_data(columns_headers, import_data),
headers=[('Content-Disposition', 'attachment; filename="%s"' % self.filename(model)),
headers=[('Content-Disposition',
content_disposition(self.filename(model), req)),
('Content-Type', self.content_type)],
cookies={'fileToken': int(token)})
@ -2004,121 +1967,13 @@ class Reports(View):
file_name = reports.read(res_id[0], ['name'], context)['name']
else:
file_name = action['report_name']
file_name = '%s.%s' % (file_name, report_struct['format'])
return req.make_response(report,
headers=[
# maybe we should take of what characters can appear in a file name?
('Content-Disposition', 'attachment; filename="%s.%s"' % (file_name, report_struct['format'])),
('Content-Disposition', content_disposition(file_name, req)),
('Content-Type', report_mimetype),
('Content-Length', len(report))],
cookies={'fileToken': int(token)})
class Import(View):
_cp_path = "/web/import"
def fields_get(self, req, model):
Model = req.session.model(model)
fields = Model.fields_get(False, req.session.eval_context(req.context))
return fields
@openerpweb.httprequest
def detect_data(self, req, csvfile, csvsep=',', csvdel='"', csvcode='utf-8', jsonp='callback'):
try:
data = list(csv.reader(
csvfile, quotechar=str(csvdel), delimiter=str(csvsep)))
except csv.Error, e:
csvfile.seek(0)
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'error': {
'message': 'Error parsing CSV file: %s' % e,
# decodes each byte to a unicode character, which may or
# may not be printable, but decoding will succeed.
# Otherwise simplejson will try to decode the `str` using
# utf-8, which is very likely to blow up on characters out
# of the ascii range (in range [128, 256))
'preview': csvfile.read(200).decode('iso-8859-1')}}))
try:
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps(
{'records': data[:10]}, encoding=csvcode))
except UnicodeDecodeError:
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({
'message': u"Failed to decode CSV file using encoding %s, "
u"try switching to a different encoding" % csvcode
}))
@openerpweb.httprequest
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp,
meta):
modle_obj = req.session.model(model)
skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')(
simplejson.loads(meta))
error = None
if not (csvdel and len(csvdel) == 1):
error = u"The CSV delimiter must be a single character"
if not indices and fields:
error = u"You must select at least one field to import"
if error:
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'error': {'message': error}}))
# skip ignored records (@skip parameter)
# then skip empty lines (not valid csv)
# nb: should these operations be reverted?
rows_to_import = itertools.ifilter(
None,
itertools.islice(
csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)),
skip, None))
# if only one index, itemgetter will return an atom rather than a tuple
if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
else: mapper = operator.itemgetter(*indices)
data = None
error = None
try:
# decode each data row
data = [
[record.decode(csvcode) for record in row]
for row in itertools.imap(mapper, rows_to_import)
# don't insert completely empty rows (can happen due to fields
# filtering in case of e.g. o2m content rows)
if any(row)
]
except UnicodeDecodeError:
error = u"Failed to decode CSV file using encoding %s" % csvcode
except csv.Error, e:
error = u"Could not process CSV file: %s" % e
# If the file contains nothing,
if not data:
error = u"File to import is empty"
if error:
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'error': {'message': error}}))
try:
(code, record, message, _nope) = modle_obj.import_data(
fields, data, 'init', '', False,
req.session.eval_context(req.context))
except xmlrpclib.Fault, e:
error = {"message": u"%s, %s" % (e.faultCode, e.faultString)}
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'error':error}))
if code != -1:
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'success':True}))
msg = u"Error during import: %s\n\nTrying to import record %r" % (
message, record)
return '<script>window.top.%s(%s);</script>' % (
jsonp, simplejson.dumps({'error': {'message':msg}}))
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -26,8 +26,7 @@ Deferreds
---------
Deferreds are a form of `promises`_. OpenERP Web currently uses
`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation
should work.
`jQuery's deferred`_.
The core idea of deferreds is that potentially asynchronous methods
will return a :js:class:`Deferred` object instead of an arbitrary
@ -44,9 +43,8 @@ directly to asynchronous methods is the ability to :ref:`compose them
Using deferreds
~~~~~~~~~~~~~~~
`CommonJS Promises/A`_ deferreds have only one method of importance:
:js:func:`Deferred.then`. This method is used to attach new callbacks
to the deferred object.
Deferreds's most important method is :js:func:`Deferred.then`. It is
used to attach new callbacks to the deferred object.
* the first parameter attaches a success callback, called when the
deferred object is successfully resolved and provided with the
@ -178,9 +176,9 @@ Deferred chaining
A second useful composition is starting an asynchronous operation as
the result of an other asynchronous operation, and wanting the result
of both: :js:func:`Deferred.then` returns the deferred on which it was
called, so handle e.g. OpenERP's search/read sequence with this would
require something along the lines of:
of both: with the tools described so far, handling e.g. OpenERP's
search/read sequence with this would require something along the lines
of:
.. code-block:: javascript
@ -195,21 +193,14 @@ require something along the lines of:
While it doesn't look too bad for trivial code, this quickly gets
unwieldy.
Instead, jQuery provides a tool to handle this kind of chains:
:js:func:`Deferred.pipe`.
:js:func:`~Deferred.pipe` has the same signature as
:js:func:`~Deferred.then` and could be used in the same manner
provided its return value was not used.
It differs from :js:func:`~Deferred.then` in two ways: it returns a
new promise object, not the one it was called with, and the return
values of the callbacks is actually important to it: whichever
callback is called,
But :js:func:`~Deferred.then` also allows handling this kind of
chains: it returns a new promise object, not the one it was called
with, and the return values of the callbacks is actually important to
it: whichever callback is called,
* If the callback is not set (not provided or left to null), the
resolution or rejection value(s) is simply forwarded to
:js:func:`~Deferred.pipe`'s promise (it's essentially a noop)
:js:func:`~Deferred.then`'s promise (it's essentially a noop)
* If the callback is set and does not return an observable object (a
deferred or a promise), the value it returns (``undefined`` if it
@ -217,7 +208,7 @@ callback is called,
.. code-block:: javascript
promise.pipe(function () {
promise.then(function () {
console.log('called');
});
@ -234,7 +225,7 @@ callback is called,
.. code-block:: javascript
return Model.search(condition).pipe(function (ids) {
return Model.search(condition).then(function (ids) {
return Model.read(ids, fields);
});
@ -243,108 +234,11 @@ callback is called,
will be resolved with ``read``'s resolution values if the chain
executes correctly.
:js:func:`~Deferred.pipe` is also useful to adapt third-party
:js:func:`~Deferred.then` is also useful to adapt third-party
promise-based APIs, in order to filter their resolution value counts
for instance (to take advantage of :js:func:`when` 's special treatment
of single-value promises).
for instance (to take advantage of :js:func:`when` 's special
treatment of single-value promises).
jQuery.Deferred API
~~~~~~~~~~~~~~~~~~~
.. js:function:: when(deferreds…)
:param deferreds: deferred objects to multiplex
:returns: a multiplexed deferred
:rtype: :js:class:`Deferred`
.. js:class:: Deferred
.. js:function:: Deferred.then(doneCallback[, failCallback])
Attaches new callbacks to the resolution or rejection of the
deferred object. Callbacks are executed in the order they are
attached to the deferred.
To provide only a failure callback, pass ``null`` as the
``doneCallback``, to provide only a success callback the
second argument can just be ignored (and not passed at all).
:param doneCallback: function called when the deferred is resolved
:type doneCallback: Function
:param failCallback: function called when the deferred is rejected
:type failCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.done(doneCallback)
Attaches a new success callback to the deferred, shortcut for
``deferred.then(doneCallback)``.
This is a jQuery extension to `CommonJS Promises/A`_ providing
little value over calling :js:func:`~Deferred.then` directly,
it should be avoided.
:param doneCallback: function called when the deferred is resolved
:type doneCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.fail(failCallback)
Attaches a new failure callback to the deferred, shortcut for
``deferred.then(null, failCallback)``.
A second jQuery extension to `Promises/A <CommonJS
Promises/A>`_. Although it provides more value than
:js:func:`~Deferred.done`, it still is not much and should be
avoided as well.
:param failCallback: function called when the deferred is rejected
:type failCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.promise()
Returns a read-only view of the deferred object, with all
mutators (resolve and reject) methods removed.
.. js:function:: Deferred.resolve(value…)
Called to resolve a deferred, any value provided will be
passed onto the success handlers of the deferred object.
Resolving a deferred which has already been resolved or
rejected has no effect.
.. js:function:: Deferred.reject(value…)
Called to reject (fail) a deferred, any value provided will be
passed onto the failure handler of the deferred object.
Rejecting a deferred which has already been resolved or
rejected has no effect.
.. js:function:: Deferred.pipe(doneFilter[, failFilter])
Filters the result of a deferred, able to transform a success
into failure and a failure into success, or to delay
resolution further.
.. [#] or simply calling :js:class:`Deferred` as a function, the
result is the same
.. [#] or not-promises, the `CommonJS Promises/B`_ role of
:js:func:`when` is to be able to treat values and promises
uniformly: :js:func:`when` will pass promises through directly,
but non-promise values and objects will be transformed into a
resolved promise (resolving themselves with the value itself).
jQuery's :js:func:`when` keeps this behavior making deferreds
easy to build from "static" values, or allowing defensive code
where expected promises are wrapped in :js:func:`when` just in
case.
.. _promises: http://en.wikipedia.org/wiki/Promise_(programming)
.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/

View File

@ -1,7 +1,7 @@
.. highlight:: javascript
Creating a new client action
============================
Client actions
==============
Client actions are the client-side version of OpenERP's "Server
Actions": instead of allowing for semi-arbitrary code to be executed

View File

@ -11,18 +11,19 @@ Contents:
.. toctree::
:maxdepth: 1
changelog-7.0
presentation
async
rpc
widget
search-view
qweb
rpc
client_action
form_view
search_view
list_view
list-view
form-notes
changelog-7.0
guides/client-action
testing

View File

@ -451,7 +451,7 @@ formview, delegating instead to its
created.
The result should be a valid form view, see :doc:`Form Notes
<form-notes>` for various peculiarities of the form view
<form_view>` for various peculiarities of the form view
format.
:param editor: editor object asking for the view

View File

@ -0,0 +1,42 @@
Presentation
============
Prerequisites
-------------
Prerequisites to code addons for the OpenERP Web Client:
- HTML5
- CSS
- Javascript (Ecmascript 5)
- jQuery
Once you know all this, that's only the beginning. Most usages of Javascript/jQuery are small hacks to make a web page nicer. The OpenERP Client Web is different: it's a web application, not a web site. It doesn't have multiple pages generated by a server side code. Only one unique empty page is loaded and all the html is generated by Javascript code, the page is never reloaded. If you never developed that kind of application you still have lot of good practices to learn.
Generic Guidelines
------------------
First, here are some generic recommandations about web 2.0 applications:
* **Do not use ids**. Html ids are evil, the key anti-feature that makes your components non-reusable. You want to identify a dom part? Save a jQuery object over that dom element. If you really need to use ids, use _.uniqueId(), but 99% of the time you don't need to, classes are sufficient.
* **Do not use predictable css class names** like "content" or "navigation", ten other developers will have the same idea than you and there will be clashes. A simple way to avoid this is to use prefixes. For example, if you need a css class for the button named "new" that is contained in the form view which is itself contained in the OpenERP application, name it "oe-form-view-new-button".
* **Do not use global selectors** like *$(".my-class")*, you never know if your component will be instantiated multiple times. Limit the selector with a context, like *$(".my-class", this.$el)*.
* As a general advice, **Never assume you own the page**. When you create a component, it is never unique and is always surrounded by a bunch of crazy html. You have to do with it.
* **Learn how to use jQuery's deferreds** [1]_. That concept may seem over-complicated, but the experience tell us that it is nearly impossible to create big-size javascript applications without that.
OpenERP guidelines
------------------
More recommendations related to the specific case of the OpenERP Web Client:
* The code should conform to EcmasScript 5 without the "use strict" mode as we support IE9.
* Your components should inherit from the *Widget* class. And use *Widget* 's methods (*appendTo*, *replace*,...) to insert your component and its content into the dom.
* Use QWeb templates for html rendering.
* All css classes should have the prefix *oe-* .
* Functions that call rpc() should return a deferred, even if it calls it indirectly. So a function that calls a function that calls a function that calls rpc() should return a deferred too.
.. [1] http://api.jquery.com/category/deferred-object/

79
addons/web/doc/qweb.rst Normal file
View File

@ -0,0 +1,79 @@
QWeb
====
QWeb is the template engine used by the OpenERP Web Client. It is a home made engine create by OpenERP developers. There are a few things to note about it:
* Template are rendered in javascript on the client-side, the server does nothing.
* It is an xml template engine, like Facelets_ for example. The source file must be a valid xml.
* Templates are not interpreted. There are compiled to javascript. This makes them a lot faster to render, but sometimes harder to debug.
* Most of the time it is used through the Widget class, but you can also use it directly using *openerp.web.qweb.render()* .
.. _Facelets: http://en.wikipedia.org/wiki/Facelets
Here is a typical QWeb file:
::
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="Template1">
<div>...</div>
</t>
<t t-name="Template2">
<div>...</div>
</t>
</templates>
A QWeb file contains multiple templates, they are simply identified by a name.
Here is a sample QWeb template:
::
<t t-name="UserPage">
<div>
<p>Name: <t t-esc="widget.user_name"/></p>
<p>Password: <input type="text" t-att-value="widget.password"/></p>
<p t-if="widget.is_admin">This user is an Administrator</p>
<t t-foreach="widget.roles" t-as="role">
<p>User has role: <t t-esc="role"/></p>
</t>
</div>
</t>
*widget* is a variable given to the template engine by Widget sub-classes when they decide to render their associated template, it is simply *this*. Here is the corresponding Widget sub-class:
::
UserPageWidget = openerp.base.Widget.extend({
template: "UserPage",
init: function(parent) {
this._super(parent);
this.user_name = "Xavier";
this.password = "lilo";
this.is_admin = true;
this.roles = ["Web Developer", "IE Hater", "Steve Jobs Worshiper"];
},
});
It could output something like this:
::
<div>
<p>Name: Xavier</p>
<p>Password: <input type="text" value="lilo"/></p>
<p>This user is an Administrator</p
<p>User has role: Web Developer</p>
<p>User has role: IE Hater</p>
<p>User has role: Steve Jobs Worshiper</p>
</div>
A QWeb template should always contain one unique root element to be used effectively with the Widget class, here it is a *<div>*. QWeb only react to *<t>* elements or attributes prefixed by *t-*. The *<t>* is simply a null element, it is only used when you need to use a *t-* attribute without outputting an html element at the same time. Here are the effects of the most common QWeb attributes:
* *t-esc* outputs the result of the evaluation of the given javascript expression
* *t-att-ATTR* sets the value of the *ATTR* attribute to the result of the evaluation of the given javascript expression
* *t-if* outputs the element and its content only if the given javascript expression returns true
* *t-foreach* outputs as many times as contained in the list returned by the given javascript expression. For each iteration, a variable with the name defined by *t-as* contains the current element in the list.

View File

@ -1,5 +1,5 @@
User Interaction: Widget
========================
Widget
======
This is the base class for all visual components. It corresponds to an MVC
view. It provides a number of services to handle a section of a page:

View File

@ -11,6 +11,7 @@ import logging
import mimetypes
import os
import pprint
import random
import sys
import tempfile
import threading
@ -21,6 +22,7 @@ import urlparse
import uuid
import xmlrpclib
import babel.core
import simplejson
import werkzeug.contrib.sessions
import werkzeug.datastructures
@ -91,9 +93,25 @@ class WebRequest(object):
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
self.session = self.httpsession.get(self.session_id)
if not self.session:
self.httpsession[self.session_id] = self.session = session.OpenERPSession()
self.session = session.OpenERPSession()
self.httpsession[self.session_id] = self.session
self.context = self.params.pop('context', None)
self.debug = self.params.pop('debug', False) != False
self.debug = self.params.pop('debug', False) is not False
# Determine self.lang
lang = self.params.get('lang', None)
if lang is None:
lang = self.session.eval_context(self.context).get('lang')
if lang is None:
lang = self.httprequest.cookies.get('lang')
if lang is None:
lang = self.httprequest.accept_languages.best
if lang is None:
lang = 'en_US'
# tranform 2 letters lang like 'en' into 5 letters like 'en_US'
lang = babel.core.LOCALE_ALIASES.get(lang, lang)
# we use _ as seprator where RFC2616 uses '-'
self.lang = lang.replace('-', '_')
class JsonRequest(WebRequest):
""" JSON-RPC2 over HTTP.
@ -128,10 +146,9 @@ class JsonRequest(WebRequest):
"id": null}
"""
def dispatch(self, controller, method):
def dispatch(self, method):
""" Calls the method asked for by the JSON-RPC2 or JSONP request
:param controller: the instance of the controller which received the request
:param method: the method which received the request
:returns: an utf8 encoded JSON-RPC2 or JSONP reply
@ -170,9 +187,9 @@ class JsonRequest(WebRequest):
self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
self.init(self.jsonrequest.get("params", {}))
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest))
_logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest))
response['id'] = self.jsonrequest.get('id')
response["result"] = method(controller, self, **self.params)
response["result"] = method(self, **self.params)
except session.AuthenticationError:
error = {
'code': 100,
@ -211,6 +228,10 @@ class JsonRequest(WebRequest):
_logger.debug("<--\n%s", pprint.pformat(response))
if jsonp:
# If we use jsonp, that's mean we are called from another host
# Some browser (IE and Safari) do no allow third party cookies
# We need then to manage http sessions manually.
response['httpsessionid'] = self.httpsession.sid
mime = 'application/javascript'
body = "%s(%s);" % (jsonp, simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder),)
else:
@ -230,16 +251,13 @@ def jsonrequest(f):
the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
beforehand)
"""
@functools.wraps(f)
def json_handler(controller, request):
return JsonRequest(request).dispatch(controller, f)
json_handler.exposed = True
return json_handler
f.exposed = 'json'
return f
class HttpRequest(WebRequest):
""" Regular GET/POST request
"""
def dispatch(self, controller, method):
def dispatch(self, method):
params = dict(self.httprequest.args)
params.update(self.httprequest.form)
params.update(self.httprequest.files)
@ -250,9 +268,9 @@ class HttpRequest(WebRequest):
akw[key] = value
else:
akw[key] = type(value)
_logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
try:
r = method(controller, self, **self.params)
r = method(self, **self.params)
except xmlrpclib.Fault, e:
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps({
'code': 200,
@ -317,11 +335,8 @@ def httprequest(f):
merged in the same dictionary), apart from the ``session_id``, ``context``
and ``debug`` keys (which are stripped out beforehand)
"""
@functools.wraps(f)
def http_handler(controller, request):
return HttpRequest(request).dispatch(controller, f)
http_handler.exposed = True
return http_handler
f.exposed = 'http'
return f
#----------------------------------------------------------
# Controller registration with a metaclass
@ -343,23 +358,13 @@ class Controller(object):
#----------------------------------------------------------
# Session context manager
#----------------------------------------------------------
STORES = {}
@contextlib.contextmanager
def session_context(request, storage_path, session_cookie='httpsessionid'):
session_store, session_lock = STORES.get(storage_path, (None, None))
if not session_store:
session_store = werkzeug.contrib.sessions.FilesystemSessionStore( storage_path)
session_lock = threading.Lock()
STORES[storage_path] = session_store, session_lock
sid = request.cookies.get(session_cookie)
def session_context(request, session_store, session_lock, sid):
with session_lock:
if sid:
request.session = session_store.get(sid)
else:
request.session = session_store.new()
try:
yield request.session
finally:
@ -411,6 +416,18 @@ def session_context(request, storage_path, session_cookie='httpsessionid'):
session_store.save(request.session)
def session_gc(session_store):
if random.random() < 0.001:
# we keep session one week
last_week = time.time() - 60*60*24*7
for fname in os.listdir(session_store.path):
path = os.path.join(session_store.path, fname)
try:
if os.path.getmtime(path) < last_week:
os.unlink(path)
except OSError:
pass
#----------------------------------------------------------
# WSGI Application
#----------------------------------------------------------
@ -445,22 +462,23 @@ class Root(object):
"""Root WSGI application for the OpenERP Web Client.
"""
def __init__(self):
self.httpsession_cookie = 'httpsessionid'
self.addons = {}
static_dirs = self._load_addons()
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
self.dispatch = DisableCacheMiddleware(app)
# Setup http sessions
try:
username = getpass.getuser()
except Exception:
username = "unknown"
self.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
if not os.path.exists(self.session_storage):
os.mkdir(self.session_storage, 0700)
_logger.debug('HTTP sessions stored in: %s', self.session_storage)
path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
if not os.path.exists(path):
os.mkdir(path, 0700)
self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path)
self.session_lock = threading.Lock()
_logger.debug('HTTP sessions stored in: %s', path)
def __call__(self, environ, start_response):
""" Handle a WSGI request
@ -483,8 +501,14 @@ class Root(object):
if not handler:
response = werkzeug.exceptions.NotFound()
else:
with session_context(request, self.session_storage, self.httpsession_cookie) as session:
result = handler( request)
sid = request.cookies.get('sid')
if not sid:
sid = request.args.get('sid')
session_gc(self.session_store)
with session_context(request, self.session_store, self.session_lock, sid) as session:
result = handler(request)
if isinstance(result, basestring):
headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
@ -493,7 +517,7 @@ class Root(object):
response = result
if hasattr(response, 'set_cookie'):
response.set_cookie(self.httpsession_cookie, session.sid)
response.set_cookie('sid', session.sid)
return response(environ, start_response)
@ -538,16 +562,21 @@ class Root(object):
"""
if l:
ps = '/' + '/'.join(filter(None, l))
meth = 'index'
method_name = 'index'
while ps:
c = controllers_path.get(ps)
if c:
m = getattr(c, meth, None)
if m and getattr(m, 'exposed', False):
_logger.debug("Dispatching to %s %s %s", ps, c, meth)
return m
ps, _slash, meth = ps.rpartition('/')
if not ps and meth:
method = getattr(c, method_name, None)
if method:
exposed = getattr(method, 'exposed', False)
if exposed == 'json':
_logger.debug("Dispatch json to %s %s %s", ps, c, method_name)
return lambda request: JsonRequest(request).dispatch(method)
elif exposed == 'http':
_logger.debug("Dispatch http to %s %s %s", ps, c, method_name)
return lambda request: HttpRequest(request).dispatch(method)
ps, _slash, method_name = ps.rpartition('/')
if not ps and method_name:
ps = '/'
return None

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
"X-Poedit-Language: Czech\n"
#. openerp-web

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
"Language: es\n"
#. openerp-web

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-08-24 09:16+0000\n"
"Last-Translator: Herczeg Péter <herczegp@gmail.com>\n"
"PO-Revision-Date: 2012-10-26 12:17+0000\n"
"Last-Translator: Herczeg Péter <hp@erp-cloud.hu>\n"
"Language-Team: Hungarian <hu@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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -755,7 +755,7 @@ msgstr "Választani kell legalább egy rekordot!"
#. openerp-web
#: addons/web/static/src/js/views.js:875
msgid "Uploading..."
msgstr ""
msgstr "Feltöltés"
#. openerp-web
#: addons/web/static/src/js/views.js:885

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-02-16 21:49+0000\n"
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
"PO-Revision-Date: 2012-10-22 14:36+0000\n"
"Last-Translator: Marius Marolla <mariusmarolla@areablu.net>\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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -129,56 +129,56 @@ msgstr "OpenERP - Unsupported/Community Version"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:619
msgid "less than a minute ago"
msgstr ""
msgstr "meno di un minuto fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:620
msgid "about a minute ago"
msgstr ""
msgstr "circa un minuto fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:621
#, python-format
msgid "%d minutes ago"
msgstr ""
msgstr "%d minuti fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:622
msgid "about an hour ago"
msgstr ""
msgstr "circa un'ora fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:623
#, python-format
msgid "%d hours ago"
msgstr ""
msgstr "%d ore fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:624
msgid "a day ago"
msgstr ""
msgstr "un giorno fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:625
#, python-format
msgid "%d days ago"
msgstr ""
msgstr "%d giorni fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:626
msgid "about a month ago"
msgstr ""
msgstr "circa un mese fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:627
#, python-format
msgid "%d months ago"
msgstr ""
msgstr "%d mesi fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:628
msgid "about a year ago"
msgstr ""
msgstr "circa un anno fa"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:629

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

1620
addons/web/i18n/lt.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -124,7 +124,7 @@ msgstr "За Нас"
#. openerp-web
#: addons/web/static/src/js/chrome.js:1052
msgid "OpenERP - Unsupported/Community Version"
msgstr "Versão da Comunidade / Sem suporte oficial da OpenERP S/A"
msgstr "OpenERP - Неподдржана/На заедницата Верзија"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:619

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -243,17 +243,19 @@ msgid ""
"Destination fields should only be selected once, some fields are selected "
"more than once:"
msgstr ""
"Pola docelowe muszą być wybierane tylko raz. niektóre inne pola mogą być "
"wybierane więcej niż raz:"
#. openerp-web
#: addons/web/static/src/js/data_import.js:386
msgid "*Required Fields are not selected :"
msgstr ""
msgstr "*Wymagane pola nie zostały wybrane :"
#. openerp-web
#: addons/web/static/src/js/formats.js:139
#, python-format
msgid "(%d records)"
msgstr ""
msgstr "(%d rekordów)"
#. openerp-web
#: addons/web/static/src/js/formats.js:325

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -15,8 +15,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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -485,7 +485,7 @@ msgstr "Doriti intr-adevar sa eliminati aceasta vizualizare?"
#: addons/web/static/src/js/view_editor.js:364
#, python-format
msgid "View Editor %d - %s"
msgstr "Editor Vizualizare %d - %s"
msgstr "Vezi Editor %d - %s"
#. openerp-web
#: addons/web/static/src/js/view_editor.js:367

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-02-20 07:27+0000\n"
"Last-Translator: Aleksei Motsik <Unknown>\n"
"PO-Revision-Date: 2012-11-01 12:07+0000\n"
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -129,62 +129,62 @@ msgstr "OpenERP - Не поддерживаемая/Community Версия"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:619
msgid "less than a minute ago"
msgstr ""
msgstr "меньше минуты назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:620
msgid "about a minute ago"
msgstr ""
msgstr "примерно минуту назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:621
#, python-format
msgid "%d minutes ago"
msgstr ""
msgstr "%d минут назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:622
msgid "about an hour ago"
msgstr ""
msgstr "примерно час назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:623
#, python-format
msgid "%d hours ago"
msgstr ""
msgstr "%d часов назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:624
msgid "a day ago"
msgstr ""
msgstr "день назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:625
#, python-format
msgid "%d days ago"
msgstr ""
msgstr "%d дней назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:626
msgid "about a month ago"
msgstr ""
msgstr "примерно месяц назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:627
#, python-format
msgid "%d months ago"
msgstr ""
msgstr "%d месяцев/месяца назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:628
msgid "about a year ago"
msgstr ""
msgstr "примерно год назад"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:629
#, python-format
msgid "%d years ago"
msgstr ""
msgstr "%d лет назад"
#. openerp-web
#: addons/web/static/src/js/data_export.js:6
@ -290,29 +290,29 @@ msgstr ""
#: addons/web/static/src/js/search.js:948
#, python-format
msgid "Filter on: %s"
msgstr ""
msgstr "Фильтр по: %s"
#. openerp-web
#: addons/web/static/src/js/search.js:999
msgid "Filter"
msgstr ""
msgstr "Фильтр"
#. openerp-web
#: addons/web/static/src/js/search.js:1108
#, python-format
msgid "Group by: %s"
msgstr ""
msgstr "Группировать по: %s"
#. openerp-web
#: addons/web/static/src/js/search.js:1132
msgid "GroupBy"
msgstr ""
msgstr "Группировать по"
#. openerp-web
#: addons/web/static/src/js/search.js:1267
#, python-format
msgid "Search %(field)s for: %(value)s"
msgstr ""
msgstr "Искать %(field)s для: %(value)s"
#. openerp-web
#: addons/web/static/src/js/search.js:869
@ -339,7 +339,7 @@ msgstr "Нет"
#: addons/web/static/src/js/search.js:1416
#, python-format
msgid "Search %(field)s at: %(value)s"
msgstr ""
msgstr "Искать %(field)s в: %(value)s"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1286
@ -349,13 +349,13 @@ msgstr "Фильтры"
#. openerp-web
#: addons/web/static/src/js/search.js:1762
msgid "Advanced"
msgstr ""
msgstr "Расширенный"
#. openerp-web
#: addons/web/static/src/js/search.js:1853
#, python-format
msgid "%(field)s %(operator)s \"%(value)s\""
msgstr ""
msgstr "%(field)s %(operator)s \"%(value)s\""
#. openerp-web
#: addons/web/static/src/js/search.js:1341
@ -535,7 +535,7 @@ msgstr "Удалить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:762
msgid "Duplicate"
msgstr "Клонировать"
msgstr "Дублировать"
#. openerp-web
#: addons/web/static/src/js/view_form.js:133
@ -633,12 +633,12 @@ msgstr "Добавить: "
#. openerp-web
#: addons/web/static/src/js/view_form.js:4230
msgid "Save As..."
msgstr ""
msgstr "Сохранить как..."
#. openerp-web
#: addons/web/static/src/js/view_form.js:4230
msgid "The field is empty, there's nothing to save !"
msgstr ""
msgstr "Поле пусто, нечего сохранять !"
#. openerp-web
#: addons/web/static/src/js/view_list.js:8
@ -680,7 +680,7 @@ msgstr "Внимание"
#. openerp-web
#: addons/web/static/src/js/view_list.js:716
msgid "You must select at least one record."
msgstr ""
msgstr "Вы должны выбрать хотя бы одну запись ."
#. openerp-web
#: addons/web/static/src/js/view_list.js:1243
@ -728,17 +728,17 @@ msgstr "Не удалось найти определение текущего
#. openerp-web
#: addons/web/static/src/js/views.js:716
msgid "Print"
msgstr ""
msgstr "Печать"
#. openerp-web
#: addons/web/static/src/js/views.js:717
msgid "Attachment"
msgstr ""
msgstr "Вложение"
#. openerp-web
#: addons/web/static/src/js/views.js:718 addons/web/static/src/xml/base.xml:276
msgid "More"
msgstr ""
msgstr "Еще"
#. openerp-web
#: addons/web/static/src/js/views.js:810
@ -758,12 +758,12 @@ msgstr "Вы должны выбрать хотя бы одну запись."
#. openerp-web
#: addons/web/static/src/js/views.js:875
msgid "Uploading..."
msgstr ""
msgstr "Загрузка..."
#. openerp-web
#: addons/web/static/src/js/views.js:885
msgid "Do you really want to delete this attachment ?"
msgstr ""
msgstr "Вы действительно хотите удалить это вложение?"
#. openerp-web
#: addons/web/static/src/js/views.js:962
@ -867,13 +867,13 @@ msgstr "Удалить"
#: addons/web/static/src/xml/base.xml:166
#: addons/web/static/src/xml/base.xml:329
msgid "Backup"
msgstr "Резервное копирование"
msgstr "Резервная копия"
#. openerp-web
#: addons/web/static/src/xml/base.xml:195
#: addons/web/static/src/xml/base.xml:330
msgid "Restore"
msgstr "Востановить"
msgstr "Восстановить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:332
@ -908,12 +908,12 @@ msgstr "Язык по умолчанию:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:91
msgid "Admin password:"
msgstr "Пароль Администратора:"
msgstr "Пароль администратора:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:95
msgid "Confirm password:"
msgstr "Подтверждение пароля:"
msgstr "Подтвердите пароль:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:109
@ -960,17 +960,17 @@ msgstr "Подтвердить мастер-пароль:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:325
msgid "About OpenERP"
msgstr ""
msgstr "Об OpenERP"
#. openerp-web
#: addons/web/static/src/xml/base.xml:327
msgid "Log out"
msgstr ""
msgstr "Выход"
#. openerp-web
#: addons/web/static/src/xml/base.xml:333
msgid "Activate the developer mode"
msgstr ""
msgstr "Включить режим разработчика"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1820
@ -1030,12 +1030,12 @@ msgstr "Пароль ещё раз:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:390
msgid "Open"
msgstr ""
msgstr "Открыть"
#. openerp-web
#: addons/web/static/src/xml/base.xml:390
msgid "ERP"
msgstr ""
msgstr "ERP"
#. openerp-web
#: addons/web/static/src/xml/base.xml:477
@ -1050,7 +1050,7 @@ msgstr "Просмотр Лога (доступ на чтение)"
#. openerp-web
#: addons/web/static/src/xml/base.xml:450
msgid "Toggle Form Layout Outline"
msgstr ""
msgstr "Переключить структуры макета формы"
#. openerp-web
#: addons/web/static/src/xml/base.xml:479
@ -1080,7 +1080,7 @@ msgstr "Изменить действие"
#. openerp-web
#: addons/web/static/src/xml/base.xml:486
msgid "Edit Workflow"
msgstr "Редактировать Процесс"
msgstr "Редактировать процесс"
#. openerp-web
#: addons/web/static/src/xml/base.xml:491
@ -1115,28 +1115,28 @@ msgstr "Дата изменения:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:518
msgid "Delete this attachment"
msgstr ""
msgstr "Удалить вложение"
#. openerp-web
#: addons/web/static/src/xml/base.xml:523
msgid "/web/binary/upload_attachment"
msgstr ""
msgstr "/web/binary/upload_attachment"
#. openerp-web
#: addons/web/static/src/xml/base.xml:527
msgid "Add..."
msgstr ""
msgstr "Добавить..."
#. openerp-web
#: addons/web/static/src/xml/base.xml:622
#: addons/web/static/src/xml/base.xml:687
msgid "or"
msgstr ""
msgstr "или"
#. openerp-web
#: addons/web/static/src/xml/base.xml:687
msgid "Discard"
msgstr ""
msgstr "Отменить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:806
@ -1222,7 +1222,7 @@ msgstr "При изменении:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:981
msgid "Relation:"
msgstr "Отношение:"
msgstr "Связь:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:985
@ -1242,13 +1242,13 @@ msgstr "Выбрать дату"
#. openerp-web
#: addons/web/static/src/xml/base.xml:948
msgid "Open Resource"
msgstr ""
msgstr "Открыть ресурс"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1162
#: addons/web/static/src/xml/base.xml:1205
msgid "Set Image"
msgstr "Назначить изображение"
msgstr "Установить изображение"
#. openerp-web
#: addons/web/static/src/js/view_form.js:1623
@ -1263,12 +1263,12 @@ msgstr "Очистить"
#: addons/web/static/src/xml/base.xml:1179
#: addons/web/static/src/xml/base.xml:1230
msgid "Uploading ..."
msgstr "Загружаю ..."
msgstr "Загружается ..."
#. openerp-web
#: addons/web/static/src/xml/base.xml:1066
msgid "width: 83px;"
msgstr ""
msgstr "ширина: 83px ;"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1207
@ -1320,7 +1320,7 @@ msgstr "Поле"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1205
msgid "Advanced Search..."
msgstr ""
msgstr "Расширенный поиск..."
#. openerp-web
#: addons/web/static/src/xml/base.xml:1287
@ -1355,22 +1355,22 @@ msgstr "(Имеющийся фильтр с таким же имененем б
#. openerp-web
#: addons/web/static/src/xml/base.xml:1376
msgid "Custom Filters"
msgstr ""
msgstr "Настраиваемые фильтры"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1379
msgid "Save current filter"
msgstr ""
msgstr "Сохранить текущий фильтр"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1381
msgid "Filter name"
msgstr ""
msgstr "Название фильтра"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1383
msgid "Share with all users"
msgstr ""
msgstr "Совместно со всеми пользователями"
#. openerp-web
#: addons/web/static/src/js/search.js:298
@ -1381,42 +1381,42 @@ msgstr "Добавить на Панель"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1394
msgid "Title of new Dashboard item"
msgstr ""
msgstr "Название нового элемента панели"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1395
msgid "save"
msgstr ""
msgstr "сохранить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1399
msgid "Select Dashboard to add this filter to"
msgstr ""
msgstr "Выберите панель, чтобы добавить этот фильтр"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1406
msgid "Advanced Search"
msgstr ""
msgstr "Расширенный поиск"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1411
msgid "Add a condition"
msgstr ""
msgstr "Добавить условие"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1412
msgid "Apply"
msgstr ""
msgstr "Применить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1509
msgid "Save & New"
msgstr "Сохранить и Создать"
msgstr "Сохранить и создать"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1510
msgid "Save & Close"
msgstr "Сохранить и Закрыть"
msgstr "Сохранить и закрыть"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1617
@ -1426,9 +1426,9 @@ msgid ""
" You can export all data or only the fields that can be "
"reimported after modification."
msgstr ""
"Этот мастер экспортирует все найденные данные в CSV файл.\n"
" Вы можете экспортровать все данные либо только те поля которые "
"могут быть в последствии импортированны."
"Этот мастер экспортирует все найденные данные в CSV-файл.\n"
" Вы можете экспортировать все данные, либо только те поля которые "
"должны быть в последствии импортированы после изменений."
#. openerp-web
#: addons/web/static/src/xml/base.xml:1624
@ -1556,13 +1556,13 @@ msgid ""
"For use if CSV files have titles on multiple lines, skips more than a single "
"line during import"
msgstr ""
"Применимо если в CSV файле заголовки расположены в нескольких строках и из "
"Применимо если в CSV файле заголовки расположены в нескольких строках и их "
"необходимо пропустить"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1713
msgid "--- Don't Import ---"
msgstr ""
msgstr "--- Не импортировать ---"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1809
@ -1669,9 +1669,6 @@ msgstr "Предпросмотр файла, который система не
#~ msgid "Activate the developper mode"
#~ msgstr "Активировать режим разработчика"
#~ msgid "Notebook Page \""
#~ msgstr "Страница Блокнота \""
#~ msgid "Filter disabled due to invalid syntax"
#~ msgstr "Фильтр отключен из-за неверного синтаксиса"
@ -1686,3 +1683,6 @@ msgstr "Предпросмотр файла, который система не
#~ msgid "Advanced Filters"
#~ msgstr "Расширенные фильтры"
#~ msgid "Notebook Page \""
#~ msgstr "Страница блокнота \""

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-01-31 06:25+0000\n"
"Last-Translator: ERP Basing <erp@basing.si>\n"
"PO-Revision-Date: 2012-11-01 16:38+0000\n"
"Last-Translator: Dusan Laznik <laznik@mentis.si>\n"
"Language-Team: Slovenian <sl@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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -129,62 +129,62 @@ msgstr "OpenERP-Community Version"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:619
msgid "less than a minute ago"
msgstr ""
msgstr "manj kot minuto nazaj"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:620
msgid "about a minute ago"
msgstr ""
msgstr "približno pred minuto"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:621
#, python-format
msgid "%d minutes ago"
msgstr ""
msgstr "pred %d minutami"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:622
msgid "about an hour ago"
msgstr ""
msgstr "pred približno eno uro"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:623
#, python-format
msgid "%d hours ago"
msgstr ""
msgstr "Pred %d urami"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:624
msgid "a day ago"
msgstr ""
msgstr "pred enim dnevom"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:625
#, python-format
msgid "%d days ago"
msgstr ""
msgstr "pred %d dnevi"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:626
msgid "about a month ago"
msgstr ""
msgstr "približno pred enim mesecem"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:627
#, python-format
msgid "%d months ago"
msgstr ""
msgstr "pred %d meseci"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:628
msgid "about a year ago"
msgstr ""
msgstr "približno pred enim letom"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:629
#, python-format
msgid "%d years ago"
msgstr ""
msgstr "Pred %d leti"
#. openerp-web
#: addons/web/static/src/js/data_export.js:6
@ -293,13 +293,13 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/js/search.js:999
msgid "Filter"
msgstr ""
msgstr "Filter:"
#. openerp-web
#: addons/web/static/src/js/search.js:1108
#, python-format
msgid "Group by: %s"
msgstr ""
msgstr "Združi po: %s"
#. openerp-web
#: addons/web/static/src/js/search.js:1132
@ -347,7 +347,7 @@ msgstr "Filtri"
#. openerp-web
#: addons/web/static/src/js/search.js:1762
msgid "Advanced"
msgstr ""
msgstr "Napredeno"
#. openerp-web
#: addons/web/static/src/js/search.js:1853
@ -632,7 +632,7 @@ msgstr "Dodaj: "
#. openerp-web
#: addons/web/static/src/js/view_form.js:4230
msgid "Save As..."
msgstr ""
msgstr "Shrani kot ..."
#. openerp-web
#: addons/web/static/src/js/view_form.js:4230
@ -669,7 +669,7 @@ msgstr "Skupina"
#. openerp-web
#: addons/web/static/src/js/view_list.js:549
msgid "Do you really want to remove these records?"
msgstr ""
msgstr "Res želite odstraniti te zapise?"
#. openerp-web
#: addons/web/static/src/js/views.js:925
@ -679,7 +679,7 @@ msgstr "Opozorilo"
#. openerp-web
#: addons/web/static/src/js/view_list.js:716
msgid "You must select at least one record."
msgstr ""
msgstr "Izbrati morate vsaj en zapis"
#. openerp-web
#: addons/web/static/src/js/view_list.js:1243
@ -727,17 +727,17 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/js/views.js:716
msgid "Print"
msgstr ""
msgstr "Tiskanje"
#. openerp-web
#: addons/web/static/src/js/views.js:717
msgid "Attachment"
msgstr ""
msgstr "Priponka"
#. openerp-web
#: addons/web/static/src/js/views.js:718 addons/web/static/src/xml/base.xml:276
msgid "More"
msgstr ""
msgstr "Dodatno"
#. openerp-web
#: addons/web/static/src/js/views.js:810
@ -757,7 +757,7 @@ msgstr "Izbrati morate vsaj en zapis."
#. openerp-web
#: addons/web/static/src/js/views.js:875
msgid "Uploading..."
msgstr ""
msgstr "Pošiljanje ..."
#. openerp-web
#: addons/web/static/src/js/views.js:885
@ -963,7 +963,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:327
msgid "Log out"
msgstr ""
msgstr "Odjava"
#. openerp-web
#: addons/web/static/src/xml/base.xml:333
@ -1028,7 +1028,7 @@ msgstr "Potrditev gesla:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:390
msgid "Open"
msgstr ""
msgstr "Odpri"
#. openerp-web
#: addons/web/static/src/xml/base.xml:390
@ -1123,18 +1123,18 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:527
msgid "Add..."
msgstr ""
msgstr "Dodaj ..."
#. openerp-web
#: addons/web/static/src/xml/base.xml:622
#: addons/web/static/src/xml/base.xml:687
msgid "or"
msgstr ""
msgstr "ali"
#. openerp-web
#: addons/web/static/src/xml/base.xml:687
msgid "Discard"
msgstr ""
msgstr "Opusti"
#. openerp-web
#: addons/web/static/src/xml/base.xml:806
@ -1220,7 +1220,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:981
msgid "Relation:"
msgstr ""
msgstr "Relacija:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:985
@ -1235,7 +1235,7 @@ msgstr "Odpri vir"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1063
msgid "Select date"
msgstr ""
msgstr "Izberi datum"
#. openerp-web
#: addons/web/static/src/xml/base.xml:948
@ -1298,7 +1298,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:1260
msgid "Button Type:"
msgstr ""
msgstr "Vrsta tipke:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1264
@ -1318,7 +1318,7 @@ msgstr "Polje"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1205
msgid "Advanced Search..."
msgstr ""
msgstr "Napredno iskanje ..."
#. openerp-web
#: addons/web/static/src/xml/base.xml:1287
@ -1363,7 +1363,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:1381
msgid "Filter name"
msgstr ""
msgstr "Ime filtra"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1383

View File

@ -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-10-13 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-02-17 07:29+0000\n"
"Last-Translator: Jeff Wang <wjfonhand@hotmail.com>\n"
"PO-Revision-Date: 2012-11-06 04:40+0000\n"
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176
@ -129,56 +129,56 @@ msgstr "OpenERP社区支持版"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:619
msgid "less than a minute ago"
msgstr ""
msgstr "一分钟内"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:620
msgid "about a minute ago"
msgstr ""
msgstr "大约一分钟"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:621
#, python-format
msgid "%d minutes ago"
msgstr ""
msgstr "%d 分钟之前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:622
msgid "about an hour ago"
msgstr ""
msgstr "大约一小时前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:623
#, python-format
msgid "%d hours ago"
msgstr ""
msgstr "%d 小时前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:624
msgid "a day ago"
msgstr ""
msgstr "一天前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:625
#, python-format
msgid "%d days ago"
msgstr ""
msgstr "%d 天前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:626
msgid "about a month ago"
msgstr ""
msgstr "大约一月前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:627
#, python-format
msgid "%d months ago"
msgstr ""
msgstr "%d 月前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:628
msgid "about a year ago"
msgstr ""
msgstr "大约一年前"
#. openerp-web
#: addons/web/static/src/js/coresetup.js:629
@ -247,13 +247,13 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/js/data_import.js:386
msgid "*Required Fields are not selected :"
msgstr ""
msgstr "*没有选择必须的字段:"
#. openerp-web
#: addons/web/static/src/js/formats.js:139
#, python-format
msgid "(%d records)"
msgstr ""
msgstr "%d 条记录)"
#. openerp-web
#: addons/web/static/src/js/formats.js:325

View File

@ -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-10-13 04:49+0000\n"
"X-Generator: Launchpad (build 16137)\n"
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
"X-Generator: Launchpad (build 16265)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:176

View File

@ -295,7 +295,12 @@
// Bind the window resize event when the width or height is auto or %
if (/auto|%/.test("" + options.width + options.height))
$(window).resize(function() {refresh(editor);});
$(window).resize(function() {
// CHM Note MonkeyPatch: if the DOM is not remove, refresh the cleditor
if(editor.$main.parent().parent().size()) {
refresh(editor);
}
});
// Create the iframe and resize the controls
refresh(editor);
@ -562,7 +567,7 @@
//==================
// Private Functions
//==================
// checksum - returns a checksum using the Adler-32 method
function checksum(text)
{

View File

@ -1,21 +0,0 @@
Copyright 2011 Xavier Morel. 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 XAVIER MOREL ``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 <COPYRIGHT HOLDER> 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.

View File

@ -1,59 +0,0 @@
.. -*- restructuredtext -*-
In jQuery 1.5, jQuery has introduced a Deferred object in order to
better handle callbacks.
Along with Deferred, it introduced ``jQuery.when`` which allows, among
other things, for multiplexing deferreds (waiting on multiple
deferreds at the same time).
While this is very nice if all deferreds are available at the same
point, it doesn't really work if the resolution of a deferred can
generate more deferreds, or for collections of deferreds coming from
multiple sources (which may not be aware of one another).
Deferred.queue tries to be a solution to this. It is based on the
principle of FIFO multiple-producers multiple-consumers tasks queues,
such as Python's `queue.Queue`_. Any code with a reference to the
queue can add a promise to the queue, and the queue (which is a
promise itself) will only be resolved once all promises within are
resolved.
Quickstart
----------
Deferred.queue has a very simple life cycle: it is dormant when
created, it starts working as soon as promises get added to it (via
``.push``, or by providing them directly to its constructor), and as
soon as the last promise in the queue is resolved it resolves itself.
If any promise in the queue fails, the queue itself will be rejected
(without waiting for further promises).
Once a queue has been resolved or rejected, adding new promises to it
results in an error.
API
---
``jQuery.Deferred.queue([promises...])``
Creates a new deferred queue. Can be primed with a series of promise
objects.
``.push(promise...)``
Adds promises to the queue. Returns the queue itself (can be
chained).
If the queue has already been resolved or rejected, raises an error.
``.then([doneCallbacks][, failCallbacks])``
Promise/A ``then`` method.
``.done(doneCallbacks)``
jQuery ``done`` extension to promise objects
``.fail(failCallbacks)``
jQuery ``fail`` extension to promise objects
.. _queue.Queue:
http://docs.python.org/dev/library/queue.html

View File

@ -1,34 +0,0 @@
(function ($) {
"use strict";
$.extend($.Deferred, {
queue: function () {
var queueDeferred = $.Deferred();
var promises = 0;
function resolve() {
if (--promises > 0) {
return;
}
setTimeout($.proxy(queueDeferred, 'resolve'), 0);
}
var promise = $.extend(queueDeferred.promise(), {
push: function () {
if (this.isResolved() || this.isRejected()) {
throw new Error("Can not add promises to a resolved "
+ "or rejected promise queue");
}
promises += 1;
$.when.apply(null, arguments).then(
resolve, $.proxy(queueDeferred, 'reject'));
return this;
}
});
if (arguments.length) {
promise.push.apply(promise, arguments);
}
return promise;
}
});
})(jQuery)

View File

@ -27,7 +27,8 @@
var $tip = this.tip();
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
$tip[0].className = 'tipsy openerp oe_tooltip '; // reset classname in case of dynamic gravity
$tip[0].className = 'tipsy '; // reset classname in case of dynamic gravity
$tip.openerpClass('oe_tooltip');
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
var pos = $.extend({}, this.$element.offset(), {

View File

@ -0,0 +1,31 @@
#jQuery UI Bootstrap
##Summary
This is a project I started to bring the beauty of Twitter's Bootstrap to jQuery UI widgets.
Twitter's Bootstrap was one of my favorite projects to come out of 2011, but having used it regularly it left me wanting two things:
* The ability to work side-by-side with jQuery UI (something which caused a number of widgets to break visually)
* The ability to theme jQuery UI widgets using Bootstrap styles. Whilst I love jQuery UI, I (like others) find some of the current themes to look a little dated. My hope is that this theme provides a decent alternative for others that feel the same.
To clarify, this project doesn't aim or intend to replace Twitter Bootstrap. It merely provides a jQuery UI-compatible theme inspired by Bootstrap's design. It also provides a version of Bootstrap CSS with a few (minor) sections commented out which enable the theme to work along-side it.
I welcome any and all feedback as I'd very much like this theme to be as solid as possible.
##Browser-support
All modern browsers are targeted by this theme with 'lo-res' experiences (i.e no gradients, border-radius etc.) provided for users using older browsers.
There *are* some minor known issues lingering that I'm working on, but the hope is that in time those will all get ironed out.
##jQuery UI support
This theme targets jQuery UI 1.8.16 and the default version of jQuery included in the (current) jQuery UI builds (jQuery 1.6.2). I'm aware of jQuery 1.7.1 and intend on upgrading anything necessary in the theme to use it when the jQuery UI team officially include it in their theme packs.
##Demo
For a live preview of the theme, see [http://addyosmani.github.com/jquery-ui-bootstrap](http://addyosmani.github.com/jquery-ui-bootstrap).
A [blog post](http://addyosmani.com/blog/jquery-ui-bootstrap/) with some more details about the project is also available.

View File

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 180 B

View File

Before

Width:  |  Height:  |  Size: 120 B

After

Width:  |  Height:  |  Size: 120 B

View File

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 105 B

View File

Before

Width:  |  Height:  |  Size: 111 B

After

Width:  |  Height:  |  Size: 111 B

View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

View File

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 107 B

View File

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 101 B

View File

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 123 B

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -23,6 +23,7 @@
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
@ -59,7 +60,7 @@
----------------------------------*/
.ui-widget { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size:13px; }
.ui-widget .ui-widget { font-size: 1em; }
/*.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1em; }*/
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_75_ffffff_1x400.png) 50% 50% repeat-x; color: #404040; }
.ui-widget-content a { color: #404040; }
.ui-widget-header {
@ -399,7 +400,6 @@
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
@ -432,9 +432,9 @@
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion 1.8.16
* jQuery UI Accordion 1.9.0
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
@ -445,7 +445,7 @@
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; font-weight:bold; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 1.7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
@ -465,46 +465,55 @@
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu 1.8.16
* jQuery UI Menu 1.9.0
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Copyright 2012-10-11, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
background:#0064CD;
color:#fff
.ui-menu { list-style:none; padding: 2px; margin: 0; display:block; float:left; outline: none; }
.ui-menu .ui-menu { margin-top: -3px; position: absolute; }
.ui-menu .ui-menu-item { margin: 0; padding: 0; zoom: 1;float: left;clear: left; width: 100%; }
.ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: 0;
color: #ffffff;
background: #0064cd;
background-color: #0064cd;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
background-image: -o-linear-gradient(top, #049cdb, #0064cd);
background-image: linear-gradient(top, #049cdb, #0064cd);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
border-color: #0064cd #0064cd #003f81;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.ui-menu .ui-state-disabled { font-weight: normal; margin: .4em 0 .2em; line-height: 1.5; }
.ui-menu .ui-state-disabled a { cursor: default; }
/* icon support */
.ui-menu-icons { position: relative; }
.ui-menu-icons .ui-menu-item a { position: relative; padding-left: 2em; }
/* left-aligned */
.ui-menu .ui-icon { position: absolute; top: .2em; left: .2em; }
/* right-aligned */
.ui-menu .ui-menu-icon { position: static; float: right; }
.ui-menu { width: 200px; margin-bottom: 2em; }
/*
* jQuery UI Button 1.8.16
@ -651,7 +660,31 @@ button.ui-button-icons-only { width: 3.7em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI spinner 1.9.0
*
* Copyright 2012-10-11, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-spinner { position:relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; }
.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 22px; }
.ui-spinner{}
.ui-spinner-button { width: 16px; height: 50%; font-size: .5em; padding: 0; margin: 0; z-index: 100; text-align: center; position: absolute; cursor: default; display: block; overflow: hidden; right: 0; }
.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: none; } /* more specificity required here to overide default borders */
.ui-spinner .ui-icon { position: absolute; margin-top: -8px; top: 50%; left: 0; } /* vertical centre icon */
.ui-spinner-up { top: 0; }
.ui-spinner-down { bottom: 0; }
/* TR overrides */
span.ui-spinner { background: none; }
.ui-spinner .ui-icon-triangle-1-s {
/* need to fix icons sprite */
background-position:-65px -16px;
}
/*
* jQuery UI Dialog 1.8.16
@ -805,14 +838,16 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs 1.8.16
.ui-slider-vertical .ui-slider-range-max { top: 0; }
/*
* jQuery UI Tabs 1.9.0
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
* http://jqueryui.com/tabs/
*/
.ui-tabs .ui-tabs-nav{ background:none; border-color: #ddd;
border-style: solid;
@ -824,14 +859,11 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
background:whiteSmoke;
border-bottom:1px solid #ddd;
padding-bottom:0px;
color:#4c4c4c;
color:#00438A;
}
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; border-bottom:1px solid #DDD; }
.ui-tabs .ui-tabs-nav li { text-decoration: none; list-style: none; float: left; position: relative; top: 1px; padding: 0px 0px 1px 0px; white-space: nowrap; background:none; border:0px;
}
.ui-tabs .ui-tabs-nav li { text-decoration: none; list-style: none; float: left; position: relative; top: 1px; padding: 0px 0px 1px 0px; white-space: nowrap; background:none; border:0px; }
.ui-tabs-nav .ui-state-default{
-webkit-box-shadow: 0px 0px 0px #ffffff; /* Saf3-4, iOS 4.0.2 - 4.2, Android 2.3+ */
@ -853,25 +885,24 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 0px; outline:none;}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a {
.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: 0; padding-bottom: 0px; outline:none;}
.ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a {
background-color: #ffffff;
border: 1px solid #ddd;
border-bottom-color: #ffffff;
cursor: default;
color:#4c4c4c;
color:gray;
outline:none;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected:hover{
.ui-tabs .ui-tabs-nav li.ui-tabs-active:hover{
background:#ffffff;
outline:none;
}
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; color:#0069D6; background:none; font-weight:normal; margin-bottom:-1px;}
.ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-tabs-loading a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { cursor: pointer; color:#0069D6; background:none; font-weight:normal; margin-bottom:-1px;}
/* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs-panel .ui-button{text-decoration:none;}
@ -883,16 +914,39 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
filter:none;
}
/*
* jQuery UI Datepicker 1.8.16
* jQuery UI Tooltip 1.9.0
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Copyright 2012-10-11, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
* http://jqueryui.com/tooltip/
*/
.ui-tooltip {
padding:8px;
position:absolute;
z-index:9999;
-o-box-shadow: 0 0 5px #ddd;
-moz-box-shadow: 0 0 5px #ddd;
-webkit-box-shadow: 0 0 5px #ddd;
/*box-shadow: 0 2px 5px #ddd;*/
box-shadow: inset 0 1px 0 #ffffff;
}
/* Fades and background-images don't work well together in IE6, drop the image */
* html .ui-tooltip {
background-image: none;
}
body .ui-tooltip { border-width:2px; }
/*
* jQuery UI Datepicker 1.9.0
*
* Copyright 2012-10-11, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://jqueryui.com/datepicker/
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; border:0px; font-weight: bold; width: 100%; padding: 4px 0; background-color: #f5f5f5; color: #808080; }
@ -982,7 +1036,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
}
.ui-datepicker td:hover{
color:white;
color: #ffffff;
}
.ui-datepicker td .ui-state-default {
@ -1001,21 +1055,34 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
margin-bottom:0px;
font-size:normal;
text-shadow: 0px;
color:white;
color: #ffffff;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.ui-datepicker td .ui-state-default:hover{
background:#0064cd;
color:white;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
.ui-datepicker td .ui-state-hover {
color: #ffffff;
background: #0064cd;
background-color: #0064cd;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
background-image: -o-linear-gradient(top, #049cdb, #0064cd);
background-image: linear-gradient(top, #049cdb, #0064cd);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
border-color: #0064cd #0064cd #003f81;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px;
}
/*
* jQuery UI Progressbar 1.8.16
*
@ -1048,7 +1115,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
/*** Input field styling from Bootstrap **/
input, textarea {
/* input, textarea {
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-ms-transition: border linear 0.2s, box-shadow linear 0.2s;
@ -1065,7 +1132,7 @@ input:focus, textarea:focus {
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
}
/*input[type=file]:focus, input[type=checkbox]:focus, select:focus {
input[type=file]:focus, input[type=checkbox]:focus, select:focus {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
@ -1073,8 +1140,9 @@ input:focus, textarea:focus {
}*/
/*input[type="text"],
input[type="password"],
textarea,*/
input[type="password"],*/
.ui-autocomplete-input,
/*textarea,*/
.uneditable-input {
display: inline-block;
padding: 4px;

View File

@ -1,5 +1,15 @@
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-left, .ui-corner-bottom{ border-radius:0px;}
/*
* jQuery UI Tabs 1.9.0
*
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://jqueryui.com/tabs/
*/
.ui-state-active,.ui-tabs-selected { border-radius:0px;}
.ui-tabs-selected { border-radius:0px;}
.ui-tabs .ui-tabs-nav li{ filter:none;}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -1,12 +1,8 @@
/*
* jQuery UI CSS Framework 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/*! jQuery UI - v1.9.1 - 2012-10-29
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */
/* Layout helpers
----------------------------------*/
@ -36,20 +32,198 @@
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
.ui-accordion .ui-accordion-header { display: block; cursor: pointer; position: relative; margin-top: 2px; padding: .5em .5em .5em .7em; zoom: 1; }
.ui-accordion .ui-accordion-icons { padding-left: 2.2em; }
.ui-accordion .ui-accordion-noicons { padding-left: .7em; }
.ui-accordion .ui-accordion-icons .ui-accordion-icons { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-accordion-header-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; overflow: auto; zoom: 1; }
.ui-autocomplete {
position: absolute;
top: 0; /* #8656 */
cursor: default;
}
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button, .ui-button:link, .ui-button:visited, .ui-button:hover, .ui-button:active { text-decoration: none; }
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*
* jQuery UI CSS Framework 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
.ui-menu { list-style:none; padding: 2px; margin: 0; display:block; outline: none; }
.ui-menu .ui-menu { margin-top: -3px; position: absolute; }
.ui-menu .ui-menu-item { margin: 0; padding: 0; zoom: 1; width: 100%; }
.ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; }
.ui-menu .ui-state-disabled { font-weight: normal; margin: .4em 0 .2em; line-height: 1.5; }
.ui-menu .ui-state-disabled a { cursor: default; }
/* icon support */
.ui-menu-icons { position: relative; }
.ui-menu-icons .ui-menu-item a { position: relative; padding-left: 2em; }
/* left-aligned */
.ui-menu .ui-icon { position: absolute; top: .2em; left: .2em; }
/* right-aligned */
.ui-menu .ui-menu-icon { position: static; float: right; }
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }.ui-spinner { position:relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; }
.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 22px; }
.ui-spinner-button { width: 16px; height: 50%; font-size: .5em; padding: 0; margin: 0; text-align: center; position: absolute; cursor: default; display: block; overflow: hidden; right: 0; }
.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: none; } /* more specificity required here to overide default borders */
.ui-spinner .ui-icon { position: absolute; margin-top: -8px; top: 50%; left: 0; } /* vertical centre icon */
.ui-spinner-up { top: 0; }
.ui-spinner-down { bottom: 0; }
/* TR overrides */
.ui-spinner .ui-icon-triangle-1-s {
/* need to fix icons sprite */
background-position:-65px -16px;
}
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 0; margin: 1px .2em 0 0; border-bottom: 0; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: -1px; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-tabs-loading a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tooltip {
padding: 8px;
position: absolute;
z-index: 9999;
max-width: 300px;
-webkit-box-shadow: 0 0 5px #aaa;
box-shadow: 0 0 5px #aaa;
}
/* Fades and background-images don't work well together in IE6, drop the image */
* html .ui-tooltip {
background-image: none;
}
body .ui-tooltip { border-width: 2px; }
/* Component containers
----------------------------------*/
@ -66,10 +240,9 @@
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
.ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited { color: #212121; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
@ -81,6 +254,7 @@
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
.ui-state-disabled .ui-icon { filter:Alpha(Opacity=35); } /* For IE8 - See #6059 */
/* Icons
----------------------------------*/
@ -222,8 +396,8 @@
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
@ -283,283 +457,5 @@
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
* jQuery UI Resizable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Selectable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }
/*
* jQuery UI Autocomplete 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu 1.8.17
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/*
* jQuery UI Button 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI Dialog 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
* jQuery UI Slider 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Slider#theming
*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
* jQuery UI Datepicker 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/*
* jQuery UI Progressbar 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .3;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .3;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -20,21 +20,6 @@
font-style: normal;
}
@media print {
.oe_topbar, .oe_leftbar, .oe_loading {
display: none !important;
}
}
.openerp.openerp_webclient_container {
height: 100%;
position: relative;
}
.text-tag .text-button {
height: auto !important;
min-height: 16px;
}
.openerp {
padding: 0;
margin: 0;
@ -47,6 +32,9 @@
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
*/
}
.openerp.openerp_webclient_container {
height: 100%;
}
.openerp :-moz-placeholder {
color: #afafb6 !important;
font-style: italic !important;
@ -198,6 +186,13 @@
.openerp .oe_bounce_container {
display: inline-block;
}
.openerp .text-tag .text-button {
height: auto !important;
min-height: 16px;
}
.openerp .ui-tabs {
position: static;
}
.openerp.ui-dialog {
display: none;
padding: 6px;
@ -215,7 +210,7 @@
background-clip: padding-box;
}
.openerp.ui-dialog .ui-dialog-content {
padding: 0px;
padding: 0;
}
.openerp.ui-dialog .ui-dialog-titlebar, .openerp.ui-dialog .ui-dialog-content, .openerp.ui-dialog .ui-dialog-buttonpane {
padding: 16px;
@ -657,7 +652,7 @@
cursor: pointer;
}
.openerp .oe_dropdown_toggle {
color: rgba(0, 0, 0, 0.5);
color: rgba(0, 0, 0, 0.3);
font-weight: normal;
}
.openerp .oe_dropdown_hover:hover .oe_dropdown_menu, .openerp .oe_dropdown_menu.oe_opened {
@ -1134,6 +1129,7 @@
height: 40px;
width: 157px;
margin: 14px 0;
border: 0;
}
.openerp .oe_footer {
position: fixed;
@ -1928,6 +1924,9 @@
min-height: 327px;
margin-left: -15px;
}
.openerp .oe_view.oe_cannot_create .oe_view_nocontent_create {
display: none;
}
.openerp .oe_formview {
background: white;
}
@ -2055,10 +2054,11 @@
padding-bottom: 0;
}
.openerp .oe_form div.oe_chatter {
min-width: 650px;
max-width: 860px;
box-sizing: border-box;
min-width: 682px;
max-width: 892px;
margin: 0 auto;
padding: 16px 0 48px;
padding: 16px 16px 48px;
}
.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol {
color: #aaaaaa;
@ -2300,16 +2300,16 @@
text-align: justify;
}
.openerp .oe_form_editable .oe_form .oe_form_field_integer input {
width: 6em !important;
width: 6em;
}
.openerp .oe_form_editable .oe_form .oe_form_field_float input {
width: 7em !important;
width: 7em;
}
.openerp .oe_form_editable .oe_form .oe_form_field_date input {
width: 7.5em !important;
width: 7.5em;
}
.openerp .oe_form_editable .oe_form .oe_form_field_datetime input {
width: 11.5em !important;
width: 11.5em;
}
.openerp .oe_hidden_input_file {
position: relative;
@ -2355,6 +2355,77 @@
.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
display: block;
}
.openerp .oe_fileupload {
display: inline-block;
clear: both;
width: 100%;
}
.openerp .oe_fileupload .oe_add {
float: left;
position: relative;
width: 100%;
left: 2px;
top: 7px;
}
.openerp .oe_fileupload .oe_add button {
display: inline;
height: 24px;
font-size: 12px;
line-height: 12px;
vertical-align: middle;
}
.openerp .oe_fileupload .oe_add button.oe_attach {
width: 24px;
overflow: hidden;
width: 24px;
overflow: hidden;
background: transparent;
color: #7c7bad;
box-shadow: none;
border: none;
text-shadow: none;
}
.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
position: relative;
top: -1px;
left: -9px;
}
.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
display: inline-block;
margin-left: -5px;
height: 28px;
width: 52px;
margin-top: -26px;
}
.openerp .oe_fileupload .oe_add .oe_attach_label {
color: #7c7bad;
margin-left: -3px;
}
.openerp .oe_fileupload .oe_attachments {
margin-bottom: 4px;
margin-right: 0px;
font-size: 12px;
border-radius: 2px;
border: solid 1px rgba(124, 123, 173, 0.14);
}
.openerp .oe_fileupload .oe_attachments .oe_attachment {
padding: 2px;
padding-left: 4px;
padding-right: 4px;
}
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
font-size: 23px;
margin-top: -5px;
}
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
text-decoration: none;
}
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
background: white;
}
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
background: #f4f5fa;
}
.openerp .oe_form_field_many2one td:first-child {
position: relative;
}
@ -2368,6 +2439,9 @@
float: right;
padding-left: 2px;
}
.openerp .oe_form_field_many2one input {
padding-right: 13px;
}
.openerp.ui-autocomplete li.oe_m2o_dropdown_option a {
font-style: italic;
padding-left: 2em;
@ -2513,6 +2587,9 @@
.openerp .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page, .openerp .oe_form_field_many2many > .oe_view_manager .oe_list_pager_single_page {
display: none !important;
}
.openerp .oe_form_field_one2many > .oe_view_manager .oe_view_manager_view_list, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_view_list {
min-height: 132px;
}
.openerp .oe_form_field_one2many .oe_form_field_one2many_list_row_add, .openerp .oe_form_field_many2many .oe_form_field_one2many_list_row_add {
font-weight: bold;
}
@ -2556,13 +2633,11 @@
background-color: #eeeeee;
}
.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
padding: 4px 6px 3px 6px;
}
.openerp .oe_list.oe_list_editable td.oe_list_record_delete {
position: absolute;
padding: 4px 6px 3px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
color: transparent;
text-shadow: none;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * {
visibility: hidden;
@ -2571,32 +2646,33 @@
top: 5px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_cm_button {
display: none;
line-height: 19px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_input_icon {
margin-top: 5px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field {
min-width: 0;
max-width: none;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
height: 27px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
border: 1px solid #aaaaff;
margin: 0;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field select {
min-width: 0;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
text-align: right;
width: 100% !important;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime > span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date > span {
width: 100% !important;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
width: 100% !important;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_field_float, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_view_integer, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date {
min-width: 0 !important;
max-width: none !important;
}
.openerp .oe_list_group_name {
white-space: nowrap;
}
@ -2621,8 +2697,12 @@
.openerp .oe_list_buttons.oe_editing .oe_alternative {
visibility: visible;
}
.openerp .oe_list {
position: relative;
.openerp .oe_list.oe_cannot_edit .oe_list_header_handle, .openerp .oe_list.oe_cannot_edit .oe_list_field_handle {
display: none !important;
padding: 0 !important;
}
.openerp .oe_list.oe_cannot_delete .oe_list_record_delete {
display: none !important;
}
.openerp .oe_list .oe_form .oe_form_nosheet {
margin: 0;
@ -2635,6 +2715,9 @@
margin: 0 !important;
padding: 0;
}
.openerp .oe_list .oe_form .oe_form_field_boolean {
padding: 1px 6px 3px;
}
.openerp .oe_list .oe_list_content .oe_group_header {
background-color: #fcfcfc;
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
@ -2654,6 +2737,9 @@
text-align: right !important;
max-width: 100px;
}
.openerp .oe_list_content td.oe_list_field_date, .openerp .oe_list_content th.oe_list_header_date {
min-width: 6em;
}
.openerp .oe_list_content > thead {
border-bottom: 2px solid #cacaca;
background: #eeeeee;
@ -2721,6 +2807,10 @@
.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child:after, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child:after {
border-width: 0;
}
.openerp .oe_list_content > tbody > tr > td.oe_list_field_boolean input {
filter: alpha(opacity=50);
opacity: 0.5;
}
.openerp .oe_list_content > tbody > tr:nth-child(odd) {
background-color: #f0f0fa;
background-color: #f0f0fa;
@ -2766,12 +2856,8 @@
content: "}";
color: #e0e0e0;
}
.openerp .oe_list_cannot_edit .oe_list_header_handle, .openerp .oe_list_cannot_edit .oe_list_field_handle {
display: none !important;
padding: 0 !important;
}
.openerp .oe_list_cannot_delete .oe_list_record_delete {
display: none !important;
.openerp .oe_list_content .oe_list_field_progressbar progress {
width: 100%;
}
.openerp .tree_header {
background-color: #f0f0f0;
@ -2925,3 +3011,41 @@ div.ui-widget-overlay {
-webkit-border-radius: 3px;
border-radius: 3px;
}
@media print {
.openerp {
text-shadow: none;
}
.openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default, .openerp .oe_topbar, .openerp .oe_leftbar, .openerp .oe_loading {
display: none !important;
}
.openerp .oe_list_content button, .openerp .oe_list_content input[type=checkbox] {
visibility: hidden;
}
.openerp .tree_header button, .openerp .oe_mail .oe_mail_thread_msg .oe_mail_unread, .openerp .oe_mail_fetch_more, .openerp .oe_m2o_drop_down_button img, .openerp .oe_form_field_one2many_list_row_add {
visibility: hidden;
}
.openerp a.oe_m2o_cm_button, .openerp a.oe_e {
visibility: hidden;
}
.openerp .oe_form .oe_form_field_date img, .openerp .oe_form .oe_form_field_datetime img {
visibility: hidden;
}
.openerp .oe_notebook > li.ui-tabs-selected {
display: block;
}
.openerp .oe_application .oe_form_sheet, .openerp .oe_application .oe_form_sheetbg {
border: 0px !important;
box-shadow: 0px 0px 0px;
}
.openerp .oe_view_manager_current > .oe_view_manager_header {
border: 0px !important;
box-shadow: 0px 0px 0px;
}
.openerp .text-core .text-wrap .text-arrow {
background: none;
}
.openerp .openerp div.oe_mail_wall {
overflow: hidden !important;
}
}

View File

@ -10,6 +10,8 @@ $tag-border-selected: #a6a6fe
$hover-background: #f0f0fa
$link-color: #7C7BAD
$sheet-max-width: 860px
$sheet-min-width: 650px
$sheet-padding: 16px
// }}}
// Mixins {{{
@font-face
@ -120,7 +122,7 @@ $sheet-max-width: 860px
font-size: 1px
letter-spacing: -1px
color: transparent
font-weight: normal
font-weight: normal
&:before
font: 21px "mnmliconsRegular"
content: $icon-name
@ -130,7 +132,7 @@ $sheet-max-width: 860px
font-size: 1px
letter-spacing: -1px
color: transparent
font-weight: normal
font-weight: normal
&:before
font: $size "entypoRegular"
content: $icon-name
@ -138,19 +140,6 @@ $sheet-max-width: 860px
// }}}
@media print
.oe_topbar, .oe_leftbar, .oe_loading
display: none !important
.openerp.openerp_webclient_container
height: 100%
position: relative
// jQueryUI css bug fixing
.text-tag .text-button
height: auto !important
min-height: 16px
.openerp
// Global style {{{
padding: 0
@ -160,6 +149,8 @@ $sheet-max-width: 860px
font-size: 13px
background: white
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
&.openerp_webclient_container
height: 100%
// }}}
//Placeholder style{{{
\:-moz-placeholder
@ -253,6 +244,16 @@ $sheet-max-width: 860px
.oe_bounce_container
display: inline-block
// Bug lp:1051746
.text-tag .text-button
height: auto !important
min-height: 16px
// bug noted in jquery ui CSS doesn't seem to occur in IE9,
// so remove position:relative
.ui-tabs
position: static
// Modal box
&.ui-dialog
display: none
@ -266,7 +267,7 @@ $sheet-max-width: 860px
@include box-shadow(0 1px 12px rgba(0, 0, 0, 0.6))
@include background-clip()
.ui-dialog-content
padding: 0px
padding: 0
.ui-dialog-titlebar, .ui-dialog-content, .ui-dialog-buttonpane
padding: 16px
.ui-dialog-titlebar
@ -563,7 +564,7 @@ $sheet-max-width: 860px
position: relative
cursor: pointer
.oe_dropdown_toggle
color: rgba(0,0,0,0.5)
color: rgba(0,0,0,0.3)
font-weight: normal
.oe_dropdown_hover:hover .oe_dropdown_menu, .oe_dropdown_menu.oe_opened
display: block
@ -608,7 +609,7 @@ $sheet-max-width: 860px
vertical-align: top
margin-top: 8px
//margin-left set at 3px to avoid a strange overflow
margin-left: 3px
margin-left: 3px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid#404040
@ -801,7 +802,7 @@ $sheet-max-width: 860px
display: none
// }}}
// Top Menu {{{
.oe_topbar
width: 100%
height: 32px
@ -922,6 +923,7 @@ $sheet-max-width: 860px
height: 40px
width: 157px
margin: 14px 0
border: 0
.oe_footer
position: fixed
bottom: 0
@ -974,7 +976,7 @@ $sheet-max-width: 860px
.oe_menu_counter
float: right
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
margin: 1px
margin: 1px
padding: 1px 4px
border: none
&:hover
@ -1066,7 +1068,7 @@ $sheet-max-width: 860px
// ViewManager common {{{
.oe_view_manager
.oe_view_manager_body
height: inherit
height: inherit
.oe_view_manager_view_kanban
height: inherit
@ -1544,7 +1546,9 @@ $sheet-max-width: 860px
padding: 100px 0 0 137px
min-height: 327px
margin-left: -15px
.oe_view.oe_cannot_create
.oe_view_nocontent_create
display: none
// }}}
// FormView.base and dynamic tags {{{
.oe_formview
@ -1576,7 +1580,7 @@ $sheet-max-width: 860px
display: none !important
.oe_form .oe_form_field_date
width: auto
// Sheet and padding
// Sheet and padding
.oe_form_nosheet
margin: 16px
> header
@ -1622,17 +1626,18 @@ $sheet-max-width: 860px
margin-left: 6px
// }}}
// FormView.custom tags and classes {{{
.oe_form
.oe_form
header
.oe_tags
margin: 5px 0 0 5px
width: 400px
padding-bottom: 0
div.oe_chatter
min-width: 650px
max-width: $sheet-max-width
box-sizing: border-box
min-width: $sheet-min-width + 2* $sheet-padding
max-width: $sheet-max-width + 2* $sheet-padding
margin: 0 auto
padding: 16px 0 48px
padding: 16px 16px 48px
div.oe_form_configuration
p, ul, ol
color: #aaa
@ -1785,7 +1790,7 @@ $sheet-max-width: 860px
display: none
.oe_datepicker_root
display: inline-block
.oe_form_required
.oe_form_required
input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])
background-color: #D2D2FF !important
.oe_form_invalid
@ -1821,20 +1826,20 @@ $sheet-max-width: 860px
position: relative
width: 600px
margin-left: 130px
margin-top: 32px
margin-top: 32px
margin-bottom: 32px
text-align: justify
.oe_form_editable
.oe_form
.oe_form_field_integer input
width: 6em !important
width: 6em
.oe_form_field_float input
width: 7em !important
width: 7em
.oe_form_field_date input
width: 7.5em !important
width: 7.5em
.oe_form_field_datetime input
width: 11.5em !important
width: 11.5em
// }}}
// FormView.fields_binary {{{
/* http://www.quirksmode.org/dom/inputfile.html
@ -1877,6 +1882,64 @@ $sheet-max-width: 860px
@include box-sizing(border)
&:hover .oe_form_field_image_controls
display: block
.oe_fileupload
display: inline-block
clear: both
width: 100%
.oe_add
float: left
position: relative
width: 100%
left: +2px
top: +7px
button
display: inline
height: 24px
font-size: 12px
line-height: 12px
vertical-align: middle
button.oe_attach
width: 24px
overflow: hidden
width: 24px
overflow: hidden
background: transparent
color: #7C7BAD
box-shadow: none
border: none
text-shadow: none
.oe_e
position: relative
top: -1px
left: -9px
input.oe_form_binary_file
display: inline-block
margin-left: -5px
height: 28px
width: 52px
margin-top: -26px
.oe_attach_label
color: #7C7BAD
margin-left: -3px
.oe_attachments
margin-bottom: 4px
margin-right: 0px
font-size: 12px
border-radius: 2px
border: solid 1px rgba(124,123,173,0.14)
.oe_attachment
padding: 2px
padding-left: 4px
padding-right: 4px
.oe_e
font-size: 23px
margin-top: -5px
.oe_e:hover
text-decoration: none
.oe_attachment:nth-child(odd)
background: white
.oe_attachment:nth-child(even)
background: #F4F5FA
// }}}
// FormView.many2one {{{
.oe_form_field_many2one
@ -1890,6 +1953,8 @@ $sheet-max-width: 860px
line-height: 14px
float: right
padding-left: 2px
input
padding-right: 13px
&.ui-autocomplete
li.oe_m2o_dropdown_option a
font-style: italic
@ -1902,7 +1967,7 @@ $sheet-max-width: 860px
display: inline-block
margin: 0
padding: 0 18px 0 0
li
li
display: inline-block
list-style-type: none
// It is probably a bad idea to modify this margin
@ -1932,7 +1997,7 @@ $sheet-max-width: 860px
vertical-align: top
overflow: hidden
margin-left: -5px
span
span
position: relative
width: 24px
height: 24px
@ -1945,7 +2010,7 @@ $sheet-max-width: 860px
@include transform(rotate(45deg))
li.oe_active
@include vertical-gradient(#729fcf, #3465a4)
.arrow span
.arrow span
@include skew-gradient(#729fcf, #3465a4)
.label
color: white
@ -1953,20 +2018,20 @@ $sheet-max-width: 860px
ul.oe_form_status_clickable
li
cursor: pointer
&:hover
&:hover
@include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%))
.label
text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede
.arrow span
.arrow span
@include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%))
.label
color: $link-color
li.oe_active
&:hover
&:hover
@include vertical-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%))
.label
text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4
.arrow span
.arrow span
@include skew-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%))
// }}}
// FormView.one2many {{{
@ -1978,6 +2043,9 @@ $sheet-max-width: 860px
> .oe_view_manager
.oe_list_pager_single_page
display: none !important
.oe_view_manager_view_list
min-height: 132px
.oe_form_field_one2many_list_row_add
font-weight: bold
.oe_list_content
@ -2018,41 +2086,37 @@ $sheet-max-width: 860px
background-color: #eee
$row-height: 27px
.oe_list_editable
.oe_list_content
td.oe_list_field_cell
padding: 4px 6px 3px 6px
.oe_list.oe_list_editable
td.oe_list_record_delete
position: absolute
.oe_list_editable .oe_list_content td.oe_list_field_cell
padding: 4px 6px 3px
.oe_list.oe_list_editable.oe_editing
.oe_edition .oe_list_field_cell:not(.oe_readonly)
*
visibility: hidden
color: transparent
text-shadow: none
.oe_m2o_drop_down_button
top: 5px
.oe_m2o_cm_button
display: none
line-height: 19px
.oe_input_icon
margin-top: 5px
.oe_form_field
min-width: 0
max-width: none
input, textarea
height: $row-height
input, textarea
@include radius(0)
border: 1px solid #aaf
margin: 0
input, textarea, select
min-width: 0
&.oe_form_field_float,&.oe_form_view_integer
input
text-align: right
width: 100% !important
&.oe_form_field_datetime,&.oe_form_field_date
> span
width: 100% !important
input.oe_datepicker_master
width: 100% !important
.oe_form_field_float,.oe_form_view_integer,&.oe_form_field_datetime,&.oe_form_field_date
min-width: 0 !important
max-width: none !important
.oe_list_group_name
white-space: nowrap
// }}}
@ -2078,7 +2142,14 @@ $sheet-max-width: 860px
visibility: visible
.oe_list
position: relative
&.oe_cannot_edit
.oe_list_header_handle, .oe_list_field_handle
display: none !important
padding: 0 !important
&.oe_cannot_delete
.oe_list_record_delete
display: none !important
.oe_form
.oe_form_nosheet
margin: 0 // FIXME: either class or border should not be by default
@ -2089,6 +2160,10 @@ $sheet-max-width: 860px
position: absolute
margin: 0 !important // dammit
padding: 0
.oe_form_field_boolean
// use padding similar to actual cell to correctly position the
// checkbox
padding: 1px 6px 3px
.oe_list_content .oe_group_header
@include vertical-gradient(#fcfcfc, #dedede)
@ -2100,6 +2175,8 @@ $sheet-max-width: 860px
td.oe_number
text-align: right !important
max-width: 100px
td.oe_list_field_date, th.oe_list_header_date
min-width: 6em
> thead
border-bottom: 2px solid #cacaca
background: #eee
@ -2149,6 +2226,8 @@ $sheet-max-width: 860px
width: 17px
&:after
border-width: 0
> td.oe_list_field_boolean input
@include opacity()
> tr:nth-child(odd)
background-color: #f0f0fa
@include vertical-gradient(#f0f0fa, #eeeef6)
@ -2176,15 +2255,8 @@ $sheet-max-width: 860px
.oe_list_handle
@include text-to-entypo-icon("}",#E0E0E0,18px)
margin-right: 7px
.oe_list_cannot_edit
.oe_list_header_handle, .oe_list_field_handle
display: none !important
padding: 0 !important
.oe_list_cannot_delete
.oe_list_record_delete
display: none !important
.oe_list_field_progressbar progress
width: 100%
// }}}
// Tree view {{{
.tree_header
@ -2235,7 +2307,6 @@ $sheet-max-width: 860px
background-image: url(/web/static/src/img/collapse.gif)
.treeview-tr.oe-treeview-first span, .treeview-td.oe-treeview-first span
margin-left: 16px
// }}}
// Debugging stuff {{{
.oe_layout_debugging
@ -2278,14 +2349,15 @@ $sheet-max-width: 860px
background-attachment: fixed
>*
opacity: 0.70
// }}}
// jQueryUI top level {{{
// The jQuery-ui overlay and Autocomplete are outside the .openerp div, please don't add indentation !!!
div.ui-widget-overlay
background: black
@include opacity(0.3)
// TODO: I think only the overlay is problematic, the other top level widgets should use $.fn.openerpClass()
// eg: $el.autocomplete().openerpClass();
.ui-widget
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
color: #4c4c4c
@ -2300,7 +2372,7 @@ div.ui-widget-overlay
width: 100%
padding: 0
a
padding: 1px 16px
padding: 1px 16px
a.ui-corner-all
@include radius(0)
a.ui-state-active
@ -2312,6 +2384,40 @@ div.ui-widget-overlay
.ui-corner-all
@include radius(3px)
// }}}
// @media print {{{
@media print
.openerp
.oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default, .oe_topbar, .oe_leftbar, .oe_loading
// We use !important here because jQuery adds @style = display: block on elements when using $.fn.show()
display: none !important
.oe_list_content
button, input[type=checkbox]
visibility: hidden
.tree_header button, .oe_mail .oe_mail_thread_msg .oe_mail_unread, .oe_mail_fetch_more, .oe_m2o_drop_down_button img, .oe_form_field_one2many_list_row_add
visibility: hidden
a
&.oe_m2o_cm_button, &.oe_e
visibility: hidden
.oe_form
.oe_form_field_date img, .oe_form_field_datetime img
visibility: hidden
.oe_notebook > li.ui-tabs-selected
display: block
.oe_application
.oe_form_sheet, .oe_form_sheetbg
border: 0px !important
box-shadow: 0px 0px 0px
.oe_view_manager_current > .oe_view_manager_header
border: 0px !important
box-shadow: 0px 0px 0px
text-shadow: none
.text-core .text-wrap .text-arrow
background: none
.openerp div.oe_mail_wall
overflow: hidden !important
// }}}
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:

View File

@ -48,7 +48,7 @@ instance.web.Notification = instance.web.Widget.extend({
*/
instance.web.dialog = function(element) {
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
result.dialog("widget").addClass("openerp");
result.dialog("widget").openerpClass();
return result;
};
@ -81,9 +81,6 @@ instance.web.Dialog = instance.web.Widget.extend({
}
}
if (options) {
if (options.buttons) {
this.params_buttons = true;
}
_.extend(this.dialog_options, options);
}
this.on("closing", this, this._closing);
@ -129,6 +126,8 @@ instance.web.Dialog = instance.web.Widget.extend({
if (! this.dialog_inited)
this.init_dialog();
var o = this.get_options(options);
this.add_buttons(o.buttons);
delete(o.buttons);
this.$buttons.appendTo($("body"));
instance.web.dialog(this.$el, o).dialog('open');
this.$el.dialog("widget").find(".ui-dialog-buttonpane").remove();
@ -138,22 +137,30 @@ instance.web.Dialog = instance.web.Widget.extend({
}
return this;
},
add_buttons: function(buttons) {
var self = this;
_.each(buttons, function(fn, but) {
var $but = $(QWeb.render('WidgetButton', { widget : { string: but, node: { attrs: {} }}}));
self.$buttons.append($but);
$but.on('click', function(ev) {
fn.call(self.$el, ev);
});
});
},
init_dialog: function(options) {
this.renderElement();
var o = this.get_options(options);
instance.web.dialog(this.$el, o);
if (! this.params_buttons) {
this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
this.$el.dialog("widget").append(this.$buttons);
} else {
this.$buttons = this.$el.dialog("widget").find(".ui-dialog-buttonpane");
}
this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
this.$el.dialog("widget").append(this.$buttons);
this.dialog_inited = true;
var res = this.start();
return res;
},
close: function() {
this.$el.dialog('close');
if (this.dialog_inited && this.$el.is(":data(dialog)")) {
this.$el.dialog('close');
}
},
_closing: function() {
if (this.__tmp_dialog_destroying)
@ -175,15 +182,22 @@ instance.web.Dialog = instance.web.Widget.extend({
this.close();
this.__tmp_dialog_destroying = undefined;
}
if (! this.isDestroyed()) {
if (this.dialog_inited && !this.isDestroyed()) {
this.$el.dialog('destroy');
}
this._super();
}
});
instance.web.CrashManager = instance.web.CallbackEnabled.extend({
instance.web.CrashManager = instance.web.Class.extend({
init: function() {
this.active = true;
},
rpc_error: function(error) {
if (!this.active) {
return;
}
if (error.data.fault_code) {
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
if (split.length > 1) {
@ -198,6 +212,9 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
}
},
show_warning: function(error) {
if (!this.active) {
return;
}
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
buttons: [
@ -206,7 +223,9 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
});
},
show_error: function(error) {
var self = this;
if (!this.active) {
return;
}
var buttons = {};
buttons[_t("Ok")] = function() {
$(this).dialog("close");
@ -294,7 +313,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
start: function() {
var self = this;
$('.oe_secondary_menus_container,.oe_user_menu_placeholder').empty();
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
var fetch_db = this.rpc("/web/database/get_list", {}).then(
function(result) {
self.db_list = result;
},
@ -302,10 +321,10 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
ev.preventDefault();
self.db_list = null;
});
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).then(function(result) {
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).done(function(result) {
self.lang_list = result;
});
return $.when(fetch_db, fetch_langs).then(self.do_render);
return $.when(fetch_db, fetch_langs).done(self.do_render);
},
do_render: function() {
var self = this;
@ -395,7 +414,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
do_create: function(form) {
var self = this;
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}).then(function(result) {
self.rpc("/web/database/create", {'fields': fields}).done(function(result) {
var form_obj = self.to_object(fields);
var client_action = {
type: 'ir.actions.client',
@ -433,7 +452,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
return;
}
self.rpc("/web/database/drop", {'fields': fields}).then(function(result) {
self.rpc("/web/database/drop", {'fields': fields}).done(function(result) {
if (result.error) {
self.display_error(result);
return;
@ -496,7 +515,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
var self = this;
self.rpc("/web/database/change_password", {
'fields': $(form).serializeArray()
}).then(function(result) {
}).done(function(result) {
if (result.error) {
self.display_error(result);
return;
@ -517,13 +536,16 @@ instance.web.Login = instance.web.Widget.extend({
template: "Login",
remember_credentials: true,
init: function(parent, params) {
init: function(parent, action) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
this.db_list = null;
this.selected_db = null;
this.selected_login = null;
this.params = params || {};
this.params = action.params || {};
if (_.isEmpty(this.params)) {
this.params = $.bbq.getState(true);
}
if (this.params.login_successful) {
this.on('login_successful', this, this.params.login_successful);
@ -554,7 +576,7 @@ instance.web.Login = instance.web.Widget.extend({
return d;
},
on_db_loaded: function (result) {
this.db_list = result.db_list;
this.db_list = result;
this.$("[name=db]").replaceWith(QWeb.render('Login.dblist', { db_list: this.db_list, selected_db: this.selected_db}));
if(this.db_list.length === 0) {
this.do_action("database_manager");
@ -594,7 +616,7 @@ instance.web.Login = instance.web.Widget.extend({
var self = this;
self.hide_error();
self.$(".oe_login_pane").fadeOut("slow");
return this.session.session_authenticate(db, login, password).pipe(function() {
return this.session.session_authenticate(db, login, password).then(function() {
if (self.has_local_storage) {
if(self.remember_credentials) {
localStorage.setItem('last_db_login_success', db);
@ -626,11 +648,38 @@ instance.web.Login = instance.web.Widget.extend({
instance.web.client_actions.add("login", "instance.web.Login");
/**
* Client action to reload the whole interface.
* If params has an entry 'menu_id', it opens the given menu entry.
* Redirect to url by replacing window.location
* If wait is true, sleep 1s and wait for the server i.e. after a restart.
*/
instance.web.Reload = function(parent, params) {
var menu_id = (params && params.menu_id) || false;
instance.web.redirect = function(url, wait) {
// Dont display a dialog if some xmlhttprequest are in progress
if (instance.client && instance.client.crashmanager) {
instance.client.crashmanager.active = false;
}
var wait_server = function() {
instance.session.rpc("/web/webclient/version_info", {}).done(function() {
window.location = url;
}).fail(function() {
setTimeout(wait_server, 250);
});
};
if (wait) {
setTimeout(wait_server, 1000);
} else {
window.location = url;
}
};
/**
* Client action to reload the whole interface.
* If params.menu_id, it opens the given menu entry.
* If params.wait, reload will wait the openerp server to be reachable before reloading
*/
instance.web.Reload = function(parent, action) {
var params = action.params || {};
var menu_id = params.menu_id || false;
var l = window.location;
var sobj = $.deparam(l.search.substr(1));
@ -642,8 +691,8 @@ instance.web.Reload = function(parent, params) {
hash = "#menu_id=" + menu_id;
}
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
window.onerror = function() {};
window.location = url;
instance.web.redirect(url, params.wait);
};
instance.web.client_actions.add("reload", "instance.web.Reload");
@ -651,9 +700,9 @@ instance.web.client_actions.add("reload", "instance.web.Reload");
* Client action to go back in breadcrumb history.
* If can't go back in history stack, will go back to home.
*/
instance.web.HistoryBack = function(parent, params) {
instance.web.HistoryBack = function(parent) {
if (!parent.history_back()) {
window.location = '/' + (window.location.search || '');
instance.web.Home(parent);
}
};
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
@ -661,11 +710,10 @@ instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
/**
* Client action to go back home.
*/
instance.web.Home = instance.web.Widget.extend({
init: function(parent, params) {
window.location = '/' + (window.location.search || '');
}
});
instance.web.Home = function(parent, action) {
var url = '/' + (window.location.search || '');
instance.web.redirect(url, action.params && action.params.wait);
};
instance.web.client_actions.add("home", "instance.web.Home");
instance.web.ChangePassword = instance.web.Widget.extend({
@ -676,7 +724,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
submitHandler: function (form) {
self.rpc("/web/session/change_password",{
'fields': $(form).serializeArray()
}).then(function(result) {
}).done(function(result) {
if (result.error) {
self.display_error(result);
return;
@ -715,7 +763,7 @@ instance.web.Menu = instance.web.Widget.extend({
},
do_reload: function() {
var self = this;
return this.rpc("/web/menu/load", {}).then(function(r) {
return this.rpc("/web/menu/load", {}).done(function(r) {
self.menu_loaded(r);
});
},
@ -885,18 +933,18 @@ instance.web.UserMenu = instance.web.Widget.extend({
if (!self.session.uid)
return;
var func = new instance.web.Model("res.users").get_func("read");
return func(self.session.uid, ["name", "company_id"]).pipe(function(res) {
return func(self.session.uid, ["name", "company_id"]).then(function(res) {
var topbar_name = res.name;
if(instance.session.debug)
topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.session.db);
if(res.company_id[0] > 1)
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
self.$el.find('.oe_topbar_name').text(topbar_name);
var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=image_small&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
var avatar_src = self.session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: self.session.uid});
$avatar.attr('src', avatar_src);
});
};
this.update_promise = this.update_promise.pipe(fct, fct);
this.update_promise = this.update_promise.then(fct, fct);
},
on_menu_logout: function() {
this.trigger('user_logout');
@ -904,7 +952,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
on_menu_settings: function() {
var self = this;
if (!this.getParent().has_uncommitted_changes()) {
self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) {
self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }).done(function(result) {
result.res_id = instance.session.uid;
self.getParent().action_manager.do_action(result);
});
@ -912,7 +960,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
},
on_menu_about: function() {
var self = this;
self.rpc("/web/webclient/version_info", {}).then(function(res) {
self.rpc("/web/webclient/version_info", {}).done(function(res) {
var $help = $(QWeb.render("UserMenu.about", {version_info: res}));
$help.find('a.oe_activate_debug_mode').click(function (e) {
e.preventDefault();
@ -932,9 +980,10 @@ instance.web.Client = instance.web.Widget.extend({
},
start: function() {
var self = this;
return instance.session.session_bind(this.origin).pipe(function() {
return instance.session.session_bind(this.origin).then(function() {
var $e = $(QWeb.render(self._template, {}));
self.replaceElement($e);
$e.openerpClass();
self.bind_events();
return self.show_common();
});
@ -999,7 +1048,7 @@ instance.web.WebClient = instance.web.Client.extend({
},
start: function() {
var self = this;
return $.when(this._super()).pipe(function() {
return $.when(this._super()).then(function() {
self.$el.on('click', '.oe_logo', function() {
self.action_manager.do_action('home');
});
@ -1068,8 +1117,8 @@ instance.web.WebClient = instance.web.Client.extend({
},
do_reload: function() {
var self = this;
return this.session.session_reload().pipe(function () {
instance.session.load_modules(true).pipe(
return this.session.session_reload().then(function () {
instance.session.load_modules(true).then(
self.menu.proxy('do_reload')); });
},
@ -1084,7 +1133,7 @@ instance.web.WebClient = instance.web.Client.extend({
on_logout: function() {
var self = this;
if (!this.has_uncommitted_changes()) {
this.session.session_logout().then(function () {
this.session.session_logout().done(function () {
$(window).unbind('hashchange', self.on_hashchange);
self.do_push_state({});
window.location.reload();
@ -1097,7 +1146,7 @@ instance.web.WebClient = instance.web.Client.extend({
var state = $.bbq.getState(true);
if (_.isEmpty(state) || state.action == "login") {
self.menu.has_been_loaded.then(function() {
self.menu.has_been_loaded.done(function() {
var first_menu_id = self.menu.$el.find("a:first").data("menu");
if(first_menu_id) {
self.menu.menu_click(first_menu_id);
@ -1111,10 +1160,10 @@ instance.web.WebClient = instance.web.Client.extend({
var self = this;
var state = event.getState(true);
if (!_.isEqual(this._current_state, state)) {
if(state.action_id === undefined && state.menu_id) {
self.menu.has_been_loaded.then(function() {
self.menu.do_reload().then(function() {
self.menu.menu_click(state.menu_id)
if(!state.action && state.menu_id) {
self.menu.has_been_loaded.done(function() {
self.menu.do_reload().done(function() {
self.menu.menu_click(state.menu_id);
});
});
} else {
@ -1130,17 +1179,19 @@ instance.web.WebClient = instance.web.Client.extend({
var url = '#' + $.param(state);
this._current_state = _.clone(state);
$.bbq.pushState(url);
this.trigger('state_pushed', state);
},
on_menu_action: function(options) {
var self = this;
return this.rpc("/web/action/load", { action_id: options.action_id })
.pipe(function (result) {
.then(function (result) {
var action = result;
if (options.needaction) {
action.context.search_default_message_unread = true;
}
return $.when(self.action_manager.do_action(action, {
clear_breadcrumbs: true,
action_menu_id: self.menu.current_menu,
})).fail(function() {
self.menu.open_menu(options.previous_menu_id);
});
@ -1179,9 +1230,9 @@ instance.web.EmbeddedClient = instance.web.Client.extend({
},
start: function() {
var self = this;
return $.when(this._super()).pipe(function() {
return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
return self.rpc("/web/action/load", { action_id: self.action_id }).then(function(result) {
return $.when(this._super()).then(function() {
return instance.session.session_authenticate(self.dbname, self.login, self.key, true).then(function() {
return self.rpc("/web/action/load", { action_id: self.action_id }).done(function(result) {
var action = result;
action.flags = _.extend({
//views_switcher : false,

View File

@ -243,15 +243,17 @@ instance.web.ParentedMixin = {
/**
* Backbone's events. Do not ever use it directly, use EventDispatcherMixin instead.
*
* 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.
*
* Copyright notice for the following Class:
*
* (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
* Backbone may be freely distributed under the MIT license.
* For all details and documentation:
* http://backbonejs.org
*
* This class just handle the dispatching of events, it is not meant to be extended,
* nor used directly. All integration with parenting and automatic unregistration of
* events is done in EventDispatcherMixin.
*
*/
var Events = instance.web.Class.extend({
@ -334,7 +336,6 @@ var Events = instance.web.Class.extend({
return this;
}
});
// end of Jeremy Ashkenas' code
instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, {
__eventDispatcherMixin: true,
@ -426,66 +427,94 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
}
});
instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, {
init: function() {
instance.web.PropertiesMixin.init.call(this);
var self = this;
// Transform on_/do_* methods into callbacks
var callback_maker = function(fn) {
return function() {
return fn.apply(self, arguments);
}
};
for (var name in this) {
if(typeof(this[name]) == "function") {
if((/^on_|^do_/).test(name)) {
this[name] = callback_maker(this[name]);
}
}
}
},
/**
* Proxies a method of the object, in order to keep the right ``this`` on
* method invocations.
*
* This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
* even more so to ``jQuery.proxy`` with a fundamental difference: its
* resolution of the method being called is lazy, meaning it will use the
* method as it is when the proxy is called, not when the proxy is created.
*
* Other methods will fix the bound method to what it is when creating the
* binding/proxy, which is fine in most javascript code but problematic in
* OpenERP Web where developers may want to replace existing callbacks with
* theirs.
*
* The semantics of this precisely replace closing over the method call.
*
* @param {String|Function} method function or name of the method to invoke
* @returns {Function} proxied method
*/
proxy: function (method) {
var self = this;
return function () {
var fn = (typeof method === 'string') ? self[method] : method;
return fn.apply(self, arguments);
}
}
});
// Classes
instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
/**
* Base class for all visual components. Provides a lot of functionalities helpful
* for the management of a part of the DOM.
*
* Widget handles:
* - Rendering with QWeb.
* - Life-cycle management and parenting (when a parent is destroyed, all its children are
* destroyed too).
* - Insertion in DOM.
*
* Guide to create implementations of the Widget class:
* ==============================================
*
* Here is a sample child class:
*
* MyWidget = instance.base.Widget.extend({
* // the name of the QWeb template to use for rendering
* template: "MyQWebTemplate",
*
* init: function(parent) {
* this._super(parent);
* // stuff that you want to init before the rendering
* },
* start: function() {
* // stuff you want to make after the rendering, `this.$el` holds a correct value
* this.$el.find(".my_button").click(/* an example of event binding * /);
*
* // if you have some asynchronous operations, it's a good idea to return
* // a promise in start()
* var promise = this.rpc(...);
* return promise;
* }
* });
*
* Now this class can simply be used with the following syntax:
*
* var my_widget = new MyWidget(this);
* my_widget.appendTo($(".some-div"));
*
* With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
* DOM inside the ".some-div" div and its events were binded.
*
* And of course, when you don't need that widget anymore, just do:
*
* my_widget.destroy();
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
// Backbone-ish API
tagName: 'div',
id: null,
className: null,
attributes: {},
events: {},
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the default render() method can not be used.
*
* @type string
*/
template: null,
/**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs instance.web.Widget
* @extends instance.web.CallbackEnabled
*
* @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
instance.web.CallbackEnabledMixin.init.call(this);
instance.web.PropertiesMixin.init.call(this);
this.setParent(parent);
// Bind on_/do_* methods to this
// We might remove this automatic binding in the future
for (var name in this) {
if(typeof(this[name]) == "function") {
if((/^on_|^do_/).test(name)) {
this[name] = this[name].bind(this);
}
}
}
// FIXME: this should not be
this.setElement(this._make_descriptive());
this.session = instance.session;
},
/**
* Destroys the current widget, also destroys all its children before destroying itself.
@ -574,94 +603,32 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
*/
start: function() {
return $.when();
}
});
// Classes
instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEnabledMixin, {
init: function() {
instance.web.CallbackEnabledMixin.init.call(this);
}
});
/**
* Base class for all visual components. Provides a lot of functionalities helpful
* for the management of a part of the DOM.
*
* Widget handles:
* - Rendering with QWeb.
* - Life-cycle management and parenting (when a parent is destroyed, all its children are
* destroyed too).
* - Insertion in DOM.
*
* Guide to create implementations of the Widget class:
* ==============================================
*
* Here is a sample child class:
*
* MyWidget = instance.base.Widget.extend({
* // the name of the QWeb template to use for rendering
* template: "MyQWebTemplate",
*
* init: function(parent) {
* this._super(parent);
* // stuff that you want to init before the rendering
* },
* start: function() {
* // stuff you want to make after the rendering, `this.$el` holds a correct value
* this.$el.find(".my_button").click(/* an example of event binding * /);
*
* // if you have some asynchronous operations, it's a good idea to return
* // a promise in start()
* var promise = this.rpc(...);
* return promise;
* }
* });
*
* Now this class can simply be used with the following syntax:
*
* var my_widget = new MyWidget(this);
* my_widget.appendTo($(".some-div"));
*
* With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
* DOM inside the ".some-div" div and its events were binded.
*
* And of course, when you don't need that widget anymore, just do:
*
* my_widget.destroy();
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
// Backbone-ish API
tagName: 'div',
id: null,
className: null,
attributes: {},
events: {},
},
/**
* 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.
* Proxies a method of the object, in order to keep the right ``this`` on
* method invocations.
*
* @type string
* This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
* even more so to ``jQuery.proxy`` with a fundamental difference: its
* resolution of the method being called is lazy, meaning it will use the
* method as it is when the proxy is called, not when the proxy is created.
*
* Other methods will fix the bound method to what it is when creating the
* binding/proxy, which is fine in most javascript code but problematic in
* OpenERP Web where developers may want to replace existing callbacks with
* theirs.
*
* The semantics of this precisely replace closing over the method call.
*
* @param {String|Function} method function or name of the method to invoke
* @returns {Function} proxied method
*/
template: null,
/**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs instance.web.Widget
* @extends instance.web.CallbackEnabled
*
* @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
instance.web.WidgetMixin.init.call(this,parent);
// FIXME: this should not be
this.setElement(this._make_descriptive());
this.session = instance.session;
proxy: function (method) {
var self = this;
return function () {
var fn = (typeof method === 'string') ? self[method] : method;
return fn.apply(self, arguments);
}
},
/**
* Renders the element. The default implementation renders the widget using QWeb,
@ -678,7 +645,6 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
}
this.replaceElement($el);
},
/**
* Re-sets the widget's root element and replaces the old root element
* (if any) by the new one in the DOM.
@ -808,13 +774,13 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
}
return false;
},
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
rpc: function(url, data, options) {
var def = $.Deferred();
var self = this;
instance.session.rpc(url, data).then(function() {
instance.session.rpc(url, data, options).done(function() {
if (!self.isDestroyed())
def.resolve.apply(def, arguments);
}, function() {
}).fail(function() {
if (!self.isDestroyed())
def.reject.apply(def, arguments);
});
@ -941,7 +907,7 @@ instance.web.Registry = instance.web.Class.extend({
}
});
instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
triggers: {
'request': 'Request sent',
'response': 'Response received',
@ -950,13 +916,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
},
/**
* @constructs instance.web.JsonRPC
* @extends instance.web.CallbackEnabled
*
* @param {String} [server] JSON-RPC endpoint hostname
* @param {String} [port] JSON-RPC endpoint port
*/
init: function() {
this._super();
instance.web.PropertiesMixin.init.call(this);
this.server = null;
this.debug = ($.deparam($.param.querystring()).debug != undefined);
},
@ -1266,12 +1231,14 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
*
* @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Object} options additional options for rpc call
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params) {
rpc: function(url, params, options) {
var self = this;
options = options || {};
// url can be an $.ajax option object
if (_.isString(url)) {
url = { url: url };
@ -1286,10 +1253,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
id: _.uniqueId('r')
};
var deferred = $.Deferred();
this.trigger('request', url, payload);
var request = this.rpc_function(url, payload).then(
if (! options.shadow)
this.trigger('request', url, payload);
var request = this.rpc_function(url, payload).done(
function (response, textStatus, jqXHR) {
self.trigger('response', response);
if (! options.shadow)
self.trigger('response', response);
if (!response.error) {
if (url.url === '/web/session/eval_domain_and_context') {
self.test_eval(params, response.result);
@ -1300,9 +1269,11 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
} else {
deferred.reject(response.error, $.Event());
}
},
}
).fail(
function(jqXHR, textStatus, errorThrown) {
self.trigger('response_failed', jqXHR);
if (! options.shadow)
self.trigger('response_failed', jqXHR);
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
@ -1310,7 +1281,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
};
deferred.reject(error, $.Event());
});
// Allow deferred user to disable on_rpc_error in fail
// Allow deferred user to disable rpc_error call in fail
deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
@ -1343,9 +1314,18 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
// extracted from payload to set on the url
var data = {
session_id: this.session_id,
id: payload.id
id: payload.id,
sid: this.httpsessionid,
};
url.url = this.get_url(url.url);
var set_sid = function (response, textStatus, jqXHR) {
// If response give us the http session id, we store it for next requests...
if (response.httpsessionid) {
self.httpsessionid = response.httpsessionid;
}
};
url.url = this.url(url.url, null);
var ajax = _.extend({
type: "GET",
dataType: 'jsonp',
@ -1360,7 +1340,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
if(payload_url.length < 2000) {
// Direct jsonp request
ajax.data.r = payload_str;
return $.ajax(ajax);
return $.ajax(ajax).done(set_sid);
} else {
// Indirect jsonp request
var ifid = _.uniqueId('oe_rpc_iframe');
@ -1387,21 +1367,31 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
$iframe.unbind('load').bind('load', function() {
$.ajax(ajax).always(function() {
cleanUp();
}).then(
function() { deferred.resolve.apply(deferred, arguments); },
function() { deferred.reject.apply(deferred, arguments); }
);
}).done(function() {
deferred.resolve.apply(deferred, arguments);
}).fail(function() {
deferred.reject.apply(deferred, arguments);
});
});
// now that the iframe can receive data, we fill and submit the form
$form.submit();
});
// append the iframe to the DOM (will trigger the first load)
$form.after($iframe);
return deferred;
return deferred.done(set_sid);
}
},
get_url: function (file) {
return this.prefix + file;
url: function(path, params) {
var qs = '';
if (!_.isNull(params)) {
params = _.extend(params || {}, {session_id: this.session_id});
if (this.httpsessionid) {
params.sid = this.httpsessionid;
}
qs = '?' + $.param(params);
}
return this.prefix + path + qs;
},
});

View File

@ -3,10 +3,13 @@
*--------------------------------------------------------*/
var console;
if (!console) {
console = {log: function () {}};
}
if (!console.debug) {
console.debug = console.log;
// Even IE9 only exposes console object if debug window opened
console = {};
('log error debug info warn assert clear dir dirxml trace group'
+ ' groupCollapsed groupEnd time timeEnd profile profileEnd count'
+ ' exception').split(/\s+/).forEach(function(property) {
console[property] = _.identity;
});
}
openerp.web.coresetup = function(instance) {
@ -19,9 +22,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
rpc: function(url, params) {
rpc: function(url, params, options) {
params.session_id = this.session_id;
return this._super(url, params);
return this._super(url, params, options);
},
/**
* Setup a sessionm
@ -51,15 +54,15 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
var self = this;
// TODO: session store in cookie should be optional
this.session_id = this.get_cookie('session_id');
return this.session_reload().pipe(function(result) {
return this.session_reload().then(function(result) {
var modules = instance._modules.join(',');
var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb);
var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).then(self.load_qweb.bind(self));
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
return deferred.then(function() { return self.load_modules(); });
}
return $.when(
deferred,
self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) {
self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).then(function(trans) {
instance.web._t.database.set_bundle(trans);
})
);
@ -73,7 +76,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
*/
session_reload: function () {
var self = this;
return this.rpc("/web/session/get_session_info", {}).then(function(result) {
return this.rpc("/web/session/get_session_info", {}).done(function(result) {
// If immediately follows a login (triggered by trying to restore
// an invalid session or no session at all), refresh session data
// (should not change, but just in case...)
@ -96,7 +99,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
var self = this;
var base_location = document.location.protocol + '//' + document.location.host;
var params = { db: db, login: login, password: password, base_location: base_location };
return this.rpc("/web/session/authenticate", params).pipe(function(result) {
return this.rpc("/web/session/authenticate", params).then(function(result) {
if (!result.uid) {
return $.Deferred().reject();
}
@ -154,30 +157,28 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
*/
load_modules: function() {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
var lang = self.user_context.lang,
all_modules = _.uniq(self.module_list.concat(result));
var params = { mods: all_modules, lang: lang};
return this.rpc('/web/session/modules', {}).then(function(result) {
var all_modules = _.uniq(self.module_list.concat(result));
var to_load = _.difference(result, self.module_list).join(',');
self.module_list = all_modules;
var loaded = self.rpc('/web/webclient/translations', params).then(function(trans) {
instance.web._t.database.set_bundle(trans);
});
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
var loaded = self.load_translations();
var datejs_locale = "/web/static/lib/datejs/globalization/" + self.user_context.lang.replace("_", "-") + ".js";
var file_list = [ datejs_locale ];
if(to_load.length) {
loaded = $.when(
loaded,
self.rpc('/web/webclient/csslist', {mods: to_load}).then(self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
self.rpc('/web/webclient/jslist', {mods: to_load}).then(function(files) {
self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)),
self.rpc('/web/webclient/qweblist', {mods: to_load}).then(self.load_qweb.bind(self)),
self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) {
file_list = file_list.concat(files);
})
);
}
return loaded.pipe(function () {
return self.do_load_js(file_list);
}).then(function() {
return loaded.then(function () {
return self.load_js(file_list);
}).done(function() {
self.on_modules_loaded();
self.trigger('module_loaded');
if (!Date.CultureInfo.pmDesignator) {
@ -190,29 +191,35 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
});
});
},
do_load_css: function (files) {
load_translations: function() {
var params = { mods: this.module_list, lang: this.user_context.lang };
return this.rpc('/web/webclient/translations', params).done(function(trans) {
instance.web._t.database.set_bundle(trans);
});
},
load_css: function (files) {
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
'href': self.get_url(file),
'href': self.url(file, null),
'rel': 'stylesheet',
'type': 'text/css'
}));
});
},
do_load_js: function(files) {
load_js: function(files) {
var self = this;
var d = $.Deferred();
if(files.length != 0) {
if(files.length !== 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = self.get_url(file);
tag.src = self.url(file, null);
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
self.do_load_js(files).then(function () {
self.load_js(files).done(function () {
d.resolve();
});
};
@ -223,11 +230,11 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
}
return d;
},
do_load_qweb: function(files) {
load_qweb: function(files) {
var self = this;
_.each(files, function(file) {
self.qweb_mutex.exec(function() {
return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
return self.rpc('/web/proxy/load', {path: file}).then(function(xml) {
if (!xml) { return; }
instance.web.qweb.add_template(_.str.trim(xml));
});
@ -453,6 +460,16 @@ $.fn.getAttributes = function() {
}
return o;
}
$.fn.openerpClass = function(additionalClass) {
// This plugin should be applied on top level elements
additionalClass = additionalClass || '';
if (!!$.browser.msie) {
additionalClass += ' openerp_ie';
}
return this.each(function() {
$(this).addClass('openerp ' + additionalClass);
});
};
/** Jquery extentions */
$.Mutex = (function() {
@ -462,7 +479,7 @@ $.Mutex = (function() {
Mutex.prototype.exec = function(action) {
var current = this.def;
var next = this.def = $.Deferred();
return current.pipe(function() {
return current.then(function() {
return $.when(action()).always(function() {
next.resolve();
});
@ -474,7 +491,7 @@ $.Mutex = (function() {
$.async_when = function() {
var async = false;
var def = $.Deferred();
$.when.apply($, arguments).then(function() {
$.when.apply($, arguments).done(function() {
var args = arguments;
var action = function() {
def.resolve.apply(def, args);
@ -483,7 +500,7 @@ $.async_when = function() {
action();
else
setTimeout(action, 0);
}, function() {
}).fail(function() {
var args = arguments;
var action = function() {
def.reject.apply(def, args);
@ -530,7 +547,8 @@ instance.web.qweb.default_dict['__debug__'] = instance.session.debug; // Which o
instance.web.qweb.debug = instance.session.debug;
instance.web.qweb.default_dict = {
'_' : _,
'_t' : instance.web._t
'_t' : instance.web._t,
'JSON': JSON,
};
instance.web.qweb.preprocess_node = function() {
// Note that 'this' is the Qweb Node

View File

@ -66,7 +66,7 @@ instance.web.Query = instance.web.Class.extend({
offset: this._offset,
limit: this._limit,
sort: instance.web.serialize_sort(this._order_by)
}).pipe(function (results) {
}).then(function (results) {
self._count = results.length;
return results.records;
}, null);
@ -78,7 +78,7 @@ instance.web.Query = instance.web.Class.extend({
*/
first: function () {
var self = this;
return this.clone({limit: 1})._execute().pipe(function (records) {
return this.clone({limit: 1})._execute().then(function (records) {
delete self._count;
if (records.length) { return records[0]; }
return null;
@ -132,9 +132,11 @@ instance.web.Query = instance.web.Class.extend({
offset: this._offset,
limit: this._limit,
orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
}).then(function (results) {
return _(results).map(function (result) {
// FIX: querygroup initialization
result.__context = result.__context || {};
result.__context.group_by = result.__context.group_by || [];
_.defaults(result.__context, ctx);
return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
@ -276,9 +278,10 @@ instance.web.Model = instance.web.Class.extend({
* @param {String} method name of the method to call
* @param {Array} [args] positional arguments
* @param {Object} [kwargs] keyword arguments
* @param {Object} [options] additional options for the rpc() method
* @returns {jQuery.Deferred<>} call result
*/
call: function (method, args, kwargs) {
call: function (method, args, kwargs, options) {
args = args || [];
kwargs = kwargs || {};
if (!_.isArray(args)) {
@ -292,7 +295,7 @@ instance.web.Model = instance.web.Class.extend({
method: method,
args: args,
kwargs: kwargs
});
}, options);
},
/**
* Fetches a Query instance bound to this model, for searching
@ -356,17 +359,16 @@ instance.web.Model = instance.web.Class.extend({
},
});
instance.web.DataSet = instance.web.CallbackEnabled.extend({
instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin, {
/**
* Collection of OpenERP records, used to share records and the current selection between views.
*
* @constructs instance.web.DataSet
* @extends instance.web.CallbackEnabled
*
* @param {String} model the OpenERP model this dataset will manage
*/
init: function(parent, model, context) {
this._super(parent);
instance.web.PropertiesMixin.init.call(this);
this.model = model;
this.context = context || {};
this.index = null;
@ -441,7 +443,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
return this._model.query(fields)
.limit(options.limit || false)
.offset(options.offset || 0)
.all().then(function (records) {
.all().done(function (records) {
self.ids = _(records).pluck('id');
});
},
@ -454,7 +456,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
*/
read_index: function (fields, options) {
options = options || {};
return this.read_ids([this.ids[this.index]], fields, options).pipe(function (records) {
return this.read_ids([this.ids[this.index]], fields, options).then(function (records) {
if (_.isEmpty(records)) { return $.Deferred().reject().promise(); }
return records[0];
});
@ -469,7 +471,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
default_get: function(fields, options) {
options = options || {};
return this._model.call('default_get',
[fields], {context: this._model.context(options.context)});
[fields], {context: this.get_context(options.context)});
},
/**
* Creates a new record in db
@ -478,7 +480,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @returns {$.Deferred}
*/
create: function(data) {
return this._model.call('create', [data], {context: this._model.context()});
return this._model.call('create', [data], {context: this.get_context()});
},
/**
* Saves the provided data in an existing db record
@ -491,7 +493,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
*/
write: function (id, data, options) {
options = options || {};
return this._model.call('write', [[id], data], {context: this._model.context(options.context)}).then(this.trigger('dataset_changed', id, data, options));
return this._model.call('write', [[id], data], {context: this.get_context(options.context)}).done(this.trigger('dataset_changed', id, data, options));
},
/**
* Deletes an existing record from the database
@ -499,7 +501,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @param {Number|String} ids identifier of the record to delete
*/
unlink: function(ids) {
return this._model.call('unlink', [ids], {context: this._model.context()}).then(this.trigger('dataset_changed', ids));
return this._model.call('unlink', [ids], {context: this.get_context()}).done(this.trigger('dataset_changed', ids));
},
/**
* Calls an arbitrary RPC method
@ -530,7 +532,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @returns {$.Deferred}
*/
name_get: function(ids) {
return this._model.call('name_get', [ids], {context: this._model.context()});
return this._model.call('name_get', [ids], {context: this.get_context()});
},
/**
*
@ -554,11 +556,10 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @param name
*/
name_create: function(name) {
return this._model.call('name_create', [name], {context: this._model.context()});
return this._model.call('name_create', [name], {context: this.get_context()});
},
exec_workflow: function (id, signal) {
return this._model.exec_workflow(id, signal)
.pipe(function (result) { return { result: result }; });
return this._model.exec_workflow(id, signal);
},
get_context: function(request_context) {
return this._model.context(request_context);
@ -605,8 +606,8 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
return instance.session.rpc('/web/dataset/resequence', {
model: this.model,
ids: ids,
context: this._model.context(options.context),
}).pipe(function (results) {
context: this.get_context(options.context),
}).then(function (results) {
return results;
});
},
@ -681,9 +682,9 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
.limit(options.limit || false);
q = q.order_by.apply(q, this._sort);
return q.all().then(function (records) {
return q.all().done(function (records) {
// FIXME: not sure about that one, *could* have discarded count
q.count().then(function (count) { self._length = count; });
q.count().done(function (count) { self._length = count; });
self.ids = _(records).pluck('id');
});
},
@ -692,7 +693,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
},
unlink: function(ids, callback, error_callback) {
var self = this;
return this._super(ids).then(function(result) {
return this._super(ids).done(function(result) {
self.ids = _(self.ids).difference(ids);
if (self._length) {
self._length -= 1;
@ -721,10 +722,10 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.last_default_get = {};
},
default_get: function(fields, options) {
return this._super(fields, options).then(this.on_default_get);
},
on_default_get: function(res) {
this.last_default_get = res;
var self = this;
return this._super(fields, options).done(function(res) {
self.last_default_get = res;
});
},
create: function(data) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
@ -773,7 +774,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);});
this.set_ids(_.without.apply(_, [this.ids].concat(ids)));
this.trigger("dataset_changed", ids, callback, error_callback);
return $.async_when({result: true}).then(callback);
return $.async_when({result: true}).done(callback);
},
reset_ids: function(ids) {
this.set_ids(ids);
@ -835,7 +836,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
completion.resolve(records);
};
if(to_get.length > 0) {
var rpc_promise = this._super(to_get, fields, options).then(function(records) {
var rpc_promise = this._super(to_get, fields, options).done(function(records) {
_.each(records, function(record, index) {
var id = to_get[index];
var cached = _.detect(self.cache, function(x) {return x.id === id;});
@ -990,14 +991,14 @@ instance.web.DropMisordered = instance.web.Class.extend({
var res = $.Deferred();
var self = this, seq = this.lsn++;
deferred.then(function () {
deferred.done(function () {
if (seq > self.rsn) {
self.rsn = seq;
res.resolve.apply(res, arguments);
} else if (self.failMisordered) {
res.reject();
}
}, function () {
}).fail(function () {
res.reject.apply(res, arguments);
});

View File

@ -51,7 +51,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.rpc("/web/export/get_fields", {
model: self.dataset.model,
import_compat: Boolean(import_comp)
}).then(function (records) {
}).done(function (records) {
got_fields.resolve();
self.on_show_data(records);
});
@ -59,7 +59,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
return $.when(
got_fields,
this.rpc('/web/export/formats', {}).then(this.do_setup_export_formats),
this.rpc('/web/export/formats', {}).done(this.do_setup_export_formats),
this.show_exports_list());
},
do_setup_export_formats: function (formats) {
@ -84,7 +84,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
return this.exports.read_slice(['name'], {
domain: [['resource', '=', this.dataset.model]]
}).then(function (export_list) {
}).done(function (export_list) {
if (!export_list.length) {
return;
}
@ -93,7 +93,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.$el.find('#fields_list option').remove();
var export_id = self.$el.find('#saved_export_list option:selected').val();
if (export_id) {
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).then(self.do_load_export_field);
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).done(self.do_load_export_field);
}
});
self.$el.find('#delete_export_list').click(function() {
@ -183,7 +183,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
import_compat: Boolean(import_comp),
parent_field_type : record['field_type'],
exclude: exclude_fields
}).then(function(results) {
}).done(function(results) {
record.loaded = true;
self.on_show_data(results, record.id);
});

View File

@ -83,6 +83,42 @@ instance.web.strip_raw_chars = function (value) {
var normalize_format = function (format) {
return Date.normalizeFormat(instance.web.strip_raw_chars(format));
};
/**
* Check with a scary heuristic if the value is a bin_size or not.
* If not, compute an approximate size out of the base64 encoded string.
*
* @param {String} value original format
*/
instance.web.binary_to_binsize = function (value) {
if (!value) {
return instance.web.human_size(0);
}
if (value.substr(0, 10).indexOf(' ') == -1) {
// Computing approximate size out of base64 encoded string
// http://en.wikipedia.org/wiki/Base64#MIME
return instance.web.human_size(value.length / 1.37);
} else {
// already bin_size
return value;
}
};
/**
* Returns a human readable size
*
* @param {Number} numner of bytes
*/
instance.web.human_size = function(size) {
var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
var i = 0;
while (size >= 1024) {
size /= 1024;
++i;
}
return size.toFixed(2) + ' ' + units[i];
};
/**
* Formats a single atomic value based on a field descriptor
*

View File

@ -124,19 +124,10 @@ function assert(condition, message) {
}
my.InputView = instance.web.Widget.extend({
template: 'SearchView.InputView',
start: function () {
var p = this._super.apply(this, arguments);
this.$el.on('focus', this.proxy('onFocus'));
this.$el.on('blur', this.proxy('onBlur'));
this.$el.on('keydown', this.proxy('onKeydown'));
return p;
},
onFocus: function () {
this.trigger('focused', this);
},
onBlur: function () {
this.$el.text('');
this.trigger('blurred', this);
events: {
focus: function () { this.trigger('focused', this); },
blur: function () { this.$el.text(''); this.trigger('blurred', this); },
keydown: 'onKeydown'
},
getSelection: function () {
// get Text node
@ -212,6 +203,27 @@ my.InputView = instance.web.Widget.extend({
});
my.FacetView = instance.web.Widget.extend({
template: 'SearchView.FacetView',
events: {
'focus': function () { this.trigger('focused', this); },
'blur': function () { this.trigger('blurred', this); },
'click': function (e) {
if ($(e.target).is('.oe_facet_remove')) {
this.model.destroy();
return false;
}
this.$el.focus();
e.stopPropagation();
},
'keydown': function (e) {
var keys = $.ui.keyCode;
switch (e.which) {
case keys.BACKSPACE:
case keys.DELETE:
this.model.destroy();
return false;
}
}
},
init: function (parent, model) {
this._super(parent);
this.model = model;
@ -223,33 +235,11 @@ my.FacetView = instance.web.Widget.extend({
},
start: function () {
var self = this;
this.$el.on('focus', function () { self.trigger('focused', self); });
this.$el.on('blur', function () { self.trigger('blurred', self); });
this.$el.on('click', function (e) {
if ($(e.target).is('.oe_facet_remove')) {
self.model.destroy();
return false;
}
self.$el.focus();
e.stopPropagation();
});
this.$el.on('keydown', function (e) {
var keys = $.ui.keyCode;
switch (e.which) {
case keys.BACKSPACE:
case keys.DELETE:
self.model.destroy();
return false;
}
});
var $e = self.$el.find('> span:last-child');
var q = $.when(this._super());
return q.pipe(function () {
var values = self.model.values.map(function (value) {
var $e = this.$('> span:last-child');
return $.when(this._super()).then(function () {
return $.when.apply(null, self.model.values.map(function (value) {
return new my.FacetValueView(self, value).appendTo($e);
});
return $.when.apply(null, values);
}));
});
},
model_changed: function () {
@ -274,6 +264,39 @@ my.FacetValueView = instance.web.Widget.extend({
instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.SearchView# */{
template: "SearchView",
events: {
// focus last input if view itself is clicked
'click': function (e) {
if (e.target === this.$('.oe_searchview_facets')[0]) {
this.$('.oe_searchview_input:last').focus();
}
},
// search button
'click button.oe_searchview_search': function (e) {
e.stopImmediatePropagation();
this.do_search();
},
'click .oe_searchview_clear': function (e) {
e.stopImmediatePropagation();
this.query.reset();
},
'click .oe_searchview_unfold_drawer': function (e) {
e.stopImmediatePropagation();
this.$el.toggleClass('oe_searchview_open_drawer');
},
'keydown .oe_searchview_input, .oe_searchview_facet': function (e) {
switch(e.which) {
case $.ui.keyCode.LEFT:
this.focusPreceding(this);
e.preventDefault();
break;
case $.ui.keyCode.RIGHT:
this.focusFollowing(this);
e.preventDefault();
break;
}
}
},
/**
* @constructs instance.web.SearchView
* @extends instance.web.Widget
@ -324,62 +347,18 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
context: this.dataset.get_context() });
$.when(load_view)
.pipe(function(r) {
.then(function(r) {
self.search_view_loaded(r)
}).fail(function () {
}, function () {
self.ready.reject.apply(null, arguments);
});
}
// Launch a search on clicking the oe_searchview_search button
this.$el.on('click', 'button.oe_searchview_search', function (e) {
e.stopImmediatePropagation();
self.do_search();
});
this.$el.on('keydown',
'.oe_searchview_input, .oe_searchview_facet', function (e) {
switch(e.which) {
case $.ui.keyCode.LEFT:
self.focusPreceding(this);
e.preventDefault();
break;
case $.ui.keyCode.RIGHT:
self.focusFollowing(this);
e.preventDefault();
break;
}
});
this.$el.on('click', '.oe_searchview_clear', function (e) {
e.stopImmediatePropagation();
self.query.reset();
});
this.$el.on('click', '.oe_searchview_unfold_drawer', function (e) {
e.stopImmediatePropagation();
self.$el.toggleClass('oe_searchview_open_drawer');
});
instance.web.bus.on('click', this, function(ev) {
if ($(ev.target).parents('.oe_searchview').length === 0) {
self.$el.removeClass('oe_searchview_open_drawer');
}
});
// Focus last input if the view itself is clicked (empty section of
// facets element)
this.$el.on('click', function (e) {
if (e.target === self.$el.find('.oe_searchview_facets')[0]) {
self.$el.find('.oe_searchview_input:last').focus();
}
});
// when the completion list opens/refreshes, automatically select the
// first completion item so if the user just hits [RETURN] or [TAB] it
// automatically selects it
this.$el.on('autocompleteopen', function () {
var menu = self.$el.data('autocomplete').menu;
menu.activate(
$.Event({ type: "mouseenter" }),
menu.element.children().first());
});
return $.when(p, this.ready);
},
@ -429,58 +408,46 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
setup_global_completion: function () {
var self = this;
// autocomplete only correctly handles being initialized on the actual
// editable element (and only an element with a @value in 1.8 e.g.
// input or textarea), cheat by setting val() on $el
this.$el.on('keydown', function () {
// keydown is triggered *before* the element's value is set, so
// delay this. Pray that setTimeout are executed in FIFO (if they
// have the same delay) as autocomplete uses the exact same trick.
// FIXME: brittle as fuck
setTimeout(function () {
self.$el.val(self.currentInputValue());
}, 0);
});
this.$el.autocomplete({
var autocomplete = this.$el.autocomplete({
source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'),
focus: function (e) { e.preventDefault(); },
html: true,
autoFocus: true,
minLength: 1,
delay: 0
}).data('autocomplete')._renderItem = function (ul, item) {
// item of completion list
var $item = $( "<li></li>" )
.data( "item.autocomplete", item )
.appendTo( ul );
}).data('autocomplete');
if (item.facet !== undefined) {
// regular completion item
return $item.append(
(item.label)
? $('<a>').html(item.label)
: $('<a>').text(item.value));
}
return $item.text(item.label)
.css({
borderTop: '1px solid #cccccc',
margin: 0,
padding: 0,
zoom: 1,
'float': 'left',
clear: 'left',
width: '100%'
});
};
},
/**
* Gets value out of the currently focused "input" (a
* div[contenteditable].oe_searchview_input)
*/
currentInputValue: function () {
return this.$el.find('div.oe_searchview_input:focus').text();
// MonkeyPatch autocomplete instance
_.extend(autocomplete, {
_renderItem: function (ul, item) {
// item of completion list
var $item = $( "<li></li>" )
.data( "item.autocomplete", item )
.appendTo( ul );
if (item.facet !== undefined) {
// regular completion item
return $item.append(
(item.label)
? $('<a>').html(item.label)
: $('<a>').text(item.value));
}
return $item.text(item.label)
.css({
borderTop: '1px solid #cccccc',
margin: 0,
padding: 0,
zoom: 1,
'float': 'left',
clear: 'left',
width: '100%'
});
},
_value: function() {
return self.$('div.oe_searchview_input').text();
},
});
},
/**
* Provide auto-completion result for req.term (an array to `resp`)
@ -510,7 +477,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
var input_index = _(this.input_subviews).indexOf(
this.subviewForRoot(
this.$el.find('div.oe_searchview_input:focus')[0]));
this.$('div.oe_searchview_input:focus')[0]));
this.query.add(ui.item.facet, {at: input_index / 2});
},
childFocused: function () {
@ -539,7 +506,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// _2: undefined if event=change, otherwise model
var self = this;
var started = [];
var $e = this.$el.find('div.oe_searchview_facets');
var $e = this.$('div.oe_searchview_facets');
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
@ -641,7 +608,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// add Filters to this.inputs, need view.controls filled
(new instance.web.search.Filters(this));
// add custom filters to this.inputs
(new instance.web.search.CustomFilters(this));
this.custom_filters = new instance.web.search.CustomFilters(this);
// add Advanced to this.inputs
(new instance.web.search.Advanced(this));
},
@ -664,20 +631,45 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// build drawer
var drawer_started = $.when.apply(
null, _(this.select_for_drawer()).invoke(
'appendTo', this.$el.find('.oe_searchview_drawer')));
'appendTo', this.$('.oe_searchview_drawer')));
// load defaults
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
'facet_for_defaults', this.defaults)).then(function () {
self.query.reset(_(arguments).compact(), {preventSearch: true});
});
'facet_for_defaults', this.defaults))
.then(this.proxy('setup_default_query'));
return $.when(drawer_started, defaults_fetched)
.then(function () {
.done(function () {
self.trigger("search_view_loaded", data);
self.ready.resolve();
});
},
setup_default_query: function () {
// Hacky implementation of CustomFilters#facet_for_defaults ensure
// CustomFilters will be ready (and CustomFilters#filters will be
// correctly filled) by the time this method executes.
var custom_filters = this.custom_filters.filters;
if (!_(custom_filters).isEmpty()) {
// Check for any is_default custom filter
var personal_filter = _(custom_filters).find(function (filter) {
return filter.user_id && filter.is_default;
});
if (personal_filter) {
this.custom_filters.enable_filter(personal_filter, true);
return;
}
var global_filter = _(custom_filters).find(function (filter) {
return !filter.user_id && filter.is_default;
});
if (global_filter) {
this.custom_filters.enable_filter(global_filter, true);
return;
}
}
// No custom filter, or no is_default custom filter, apply view defaults
this.query.reset(_(arguments).compact(), {preventSearch: true});
},
/**
* Extract search data from the view's facets.
*
@ -736,6 +728,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
*
* If at least one field failed its validation, triggers
* :js:func:`instance.web.SearchView.on_invalid` instead.
*
* @param [_query]
* @param {Object} [options]
*/
do_search: function (_query, options) {
if (options && options.preventSearch) {
@ -788,6 +783,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
instance.web.search.fields = new instance.web.Registry({
'char': 'instance.web.search.CharField',
'text': 'instance.web.search.CharField',
'html': 'instance.web.search.CharField',
'boolean': 'instance.web.search.BooleanField',
'integer': 'instance.web.search.IntegerField',
'id': 'instance.web.search.IntegerField',
@ -960,7 +956,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
*/
search_change: function () {
var self = this;
var $filters = this.$el.find('> li').removeClass('oe_selected');
var $filters = this.$('> li').removeClass('oe_selected');
var facet = this.view.query.find(_.bind(this.match_facet, this));
if (!facet) { return; }
facet.values.each(function (v) {
@ -1438,7 +1434,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
name: needle,
limit: 8,
context: {}
}).pipe(function (results) {
}).then(function (results) {
if (_.isEmpty(results)) { return null; }
return [{label: self.attrs.string}].concat(
_(results).map(function (result) {
@ -1461,7 +1457,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
// to handle this as if it were a single value.
value = value[0];
}
return this.model.call('name_get', [value]).pipe(function (names) {
return this.model.call('name_get', [value]).then(function (names) {
if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]);
})
@ -1489,10 +1485,15 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
instance.web.search.CustomFilters = instance.web.search.Input.extend({
template: 'SearchView.CustomFilters',
_in_drawer: true,
init: function () {
this.is_ready = $.Deferred();
this._super.apply(this, arguments);
},
start: function () {
var self = this;
this.model = new instance.web.Model('ir.filters');
this.filters = {};
this.$filters = {};
this.view.query
.on('remove', function (facet) {
if (!facet.get('is_custom_filter')) {
@ -1508,65 +1509,116 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
// FIXME: local eval of domain and context to get rid of special endpoint
return this.rpc('/web/searchview/get_filters', {
model: this.view.model
}).pipe(this.proxy('set_filters'));
})
.then(this.proxy('set_filters'))
.then(function () {
self.is_ready.resolve(null);
}, function () {
self.is_ready.reject();
});
},
/**
* Special implementation delaying defaults until CustomFilters is loaded
*/
facet_for_defaults: function () {
return this.is_ready;
},
/**
* Generates a mapping key (in the filters and $filter mappings) for the
* filter descriptor object provided (as returned by ``get_filters``).
*
* The mapping key is guaranteed to be unique for a given (user_id, name)
* pair.
*
* @param {Object} filter
* @param {String} filter.name
* @param {Number|Pair<Number, String>} [filter.user_id]
* @return {String} mapping key corresponding to the filter
*/
key_for: function (filter) {
var user_id = filter.user_id;
var uid = (user_id instanceof Array) ? user_id[0] : user_id;
return _.str.sprintf('(%s)%s', uid, filter.name);
},
/**
* Generates a :js:class:`~instance.web.search.Facet` descriptor from a
* filter descriptor
*
* @param {Object} filter
* @param {String} filter.name
* @param {Object} [filter.context]
* @param {Array} [filter.domain]
* @return {Object}
*/
facet_for: function (filter) {
return {
category: _t("Custom Filter"),
icon: 'M',
field: {
get_context: function () { return filter.context; },
get_groupby: function () { return [filter.context]; },
get_domain: function () { return filter.domain; }
},
is_custom_filter: true,
values: [{label: filter.name, value: null}]
};
},
clear_selection: function () {
this.$el.find('li.oe_selected').removeClass('oe_selected');
this.$('li.oe_selected').removeClass('oe_selected');
},
append_filter: function (filter) {
var self = this;
var key = _.str.sprintf('(%s)%s', filter.user_id, filter.name);
var key = this.key_for(filter);
var $filter;
if (key in this.filters) {
$filter = this.filters[key];
if (key in this.$filters) {
$filter = this.$filters[key];
} else {
var id = filter.id;
$filter = this.filters[key] = $('<li></li>')
.appendTo(this.$el.find('.oe_searchview_custom_list'))
this.filters[key] = filter;
$filter = this.$filters[key] = $('<li></li>')
.appendTo(this.$('.oe_searchview_custom_list'))
.addClass(filter.user_id ? 'oe_searchview_custom_private'
: 'oe_searchview_custom_public')
.toggleClass('oe_searchview_custom_default', filter.is_default)
.text(filter.name);
$('<a class="oe_searchview_custom_delete">x</a>')
.click(function (e) {
e.stopPropagation();
self.model.call('unlink', [id]).then(function () {
self.model.call('unlink', [id]).done(function () {
$filter.remove();
delete self.$filters[key];
delete self.filters[key];
});
})
.appendTo($filter);
}
$filter.unbind('click').click(function () {
self.view.query.reset([{
category: _t("Custom Filter"),
icon: 'M',
field: {
get_context: function () { return filter.context; },
get_groupby: function () { return [filter.context]; },
get_domain: function () { return filter.domain; }
},
is_custom_filter: true,
values: [{label: filter.name, value: null}]
}]);
$filter.addClass('oe_selected');
self.enable_filter(filter);
});
},
enable_filter: function (filter, preventSearch) {
this.view.query.reset([this.facet_for(filter)], {
preventSearch: preventSearch || false});
this.$filters[this.key_for(filter)].addClass('oe_selected');
},
set_filters: function (filters) {
_(filters).map(_.bind(this.append_filter, this));
},
save_current: function () {
var self = this;
var $name = this.$el.find('input:first');
var private_filter = !this.$el.find('input:last').prop('checked');
var $name = this.$('input:first');
var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
var search = this.view.build_search_data();
this.rpc('/web/session/eval_domain_and_context', {
domains: search.domains,
contexts: search.contexts,
group_by_seq: search.groupbys || []
}).then(function (results) {
}).done(function (results) {
if (!_.isEmpty(results.group_by)) {
results.context.group_by = results.group_by;
}
@ -1575,10 +1627,11 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
user_id: private_filter ? instance.session.uid : false,
model_id: self.view.model,
context: results.context,
domain: results.domain
domain: results.domain,
is_default: set_as_default
};
// FIXME: current context?
return self.model.call('create_or_replace', [filter]).then(function (id) {
return self.model.call('create_or_replace', [filter]).done(function (id) {
filter.id = id;
self.append_filter(filter);
self.$el
@ -1655,20 +1708,29 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
});
return $.when(
this._super(),
this.rpc("/web/searchview/fields_get", {model: this.view.model}).then(function(data) {
this.rpc("/web/searchview/fields_get", {model: this.view.model}).done(function(data) {
self.fields = _.extend({
id: { string: 'ID', type: 'id' }
}, data.fields);
})).then(function () {
})).done(function () {
self.append_proposition();
});
},
append_proposition: function () {
var self = this;
return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
.appendTo(this.$el.find('ul'));
.appendTo(this.$('ul')).done(function () {
self.$('button.oe_apply').prop('disabled', false);
});
},
remove_proposition: function (prop) {
// removing last proposition, disable apply button
if (this.getChildren().length <= 1) {
this.$('button.oe_apply').prop('disabled', true);
}
prop.destroy();
},
commit_search: function () {
var self = this;
// Get domain sections from all propositions
var children = this.getChildren();
var propositions = _.invoke(children, 'get_proposition');
@ -1698,6 +1760,13 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
template: 'SearchView.extended_search.proposition',
events: {
'change .searchview_extended_prop_field': 'changed',
'click .searchview_extended_delete_prop': function (e) {
e.stopPropagation();
this.getParent().remove_proposition(this);
}
},
/**
* @constructs instance.web.search.ExtendedSearchProposition
* @extends instance.web.Widget
@ -1715,17 +1784,10 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
this.value = null;
},
start: function () {
var _this = this;
this.$el.find(".searchview_extended_prop_field").change(function() {
_this.changed();
});
this.$el.find('.searchview_extended_delete_prop').click(function () {
_this.destroy();
});
this.changed();
return this._super().done(this.proxy('changed'));
},
changed: function() {
var nval = this.$el.find(".searchview_extended_prop_field").val();
var nval = this.$(".searchview_extended_prop_field").val();
if(this.attrs.selected == null || nval != this.attrs.selected.name) {
this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
}
@ -1740,7 +1802,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
if(this.attrs.selected != null) {
this.value.destroy();
this.value = null;
this.$el.find('.searchview_extended_prop_op').html('');
this.$('.searchview_extended_prop_op').html('');
}
this.attrs.selected = field;
if(field == null) {
@ -1756,9 +1818,9 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
_.each(this.value.operators, function(operator) {
$('<option>', {value: operator.value})
.text(String(operator.text))
.appendTo(self.$el.find('.searchview_extended_prop_op'));
.appendTo(self.$('.searchview_extended_prop_op'));
});
var $value_loc = this.$el.find('.searchview_extended_prop_value').empty();
var $value_loc = this.$('.searchview_extended_prop_value').empty();
this.value.appendTo($value_loc);
},
@ -1766,7 +1828,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
if ( this.attrs.selected == null)
return null;
var field = this.attrs.selected;
var op = this.$el.find('.searchview_extended_prop_op')[0];
var op = this.$('.searchview_extended_prop_op')[0];
var operator = op.options[op.selectedIndex];
return {
label: _.str.sprintf(_t('%(field)s %(operator)s "%(value)s"'), {

View File

@ -32,20 +32,20 @@ openerp.test_support = {
window.openerp.web[tested_core](oe);
var done = openerp.test_support.setup_session(oe.session);
if (nonliterals) {
done = done.pipe(function () {
done = done.then(function () {
return oe.session.rpc('/tests/add_nonliterals', {
domains: nonliterals.domains || [],
contexts: nonliterals.contexts || []
}).then(function (r) {
}).done(function (r) {
oe.domains = r.domains;
oe.contexts = r.contexts;
});
});
}
done.always(QUnit.start)
.then(function () {
.done(function () {
conf.openerp = oe;
}, function (e) {
}).fail(function (e) {
QUnit.test(title, function () {
console.error(e);
QUnit.ok(false, 'Could not obtain a session:' + e.debug);

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More