[MERGE]Merge lp:openerp-web.

bzr revid: vja@tinyerp.com-20121116091147-yw0ri9uwp5vi4n7c
This commit is contained in:
Vishmita 2012-11-16 14:41:47 +05:30
commit f3b0459705
323 changed files with 22016 additions and 7045 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/corelib.js",
"static/src/js/coresetup.js",
@ -55,7 +56,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",
@ -67,4 +68,5 @@ This module provides the core of the OpenERP Web Client.
'qweb' : [
"static/src/xml/*.xml",
],
'bootstrap': True,
}

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,13 +525,12 @@ 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)
@ -612,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({
@ -687,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,
@ -741,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}
@ -814,21 +766,25 @@ class Database(openerpweb.Controller):
@openerpweb.jsonrequest
def get_list(self, req):
dbs = db_list(req)
return {"db_list": dbs}
return db_list(req)
@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 drop(self, req, fields):
@ -1298,17 +1254,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"
@ -1342,7 +1287,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
@ -1516,11 +1461,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))
@ -2010,112 +1968,4 @@ class Reports(View):
('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
Indices and tables
==================

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.
@ -210,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:
@ -336,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:
@ -404,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
#----------------------------------------------------------
@ -438,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
@ -476,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))]
@ -486,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)

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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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

@ -9,13 +9,13 @@ msgstr ""
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-26 12:17+0000\n"
"Last-Translator: Herczeg Péter <herczegp@gmail.com>\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-27 05:15+0000\n"
"X-Generator: Launchpad (build 16194)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-23 05:05+0000\n"
"X-Generator: Launchpad (build 16179)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-22 04:46+0000\n"
"X-Generator: Launchpad (build 16165)\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
@ -66,7 +66,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/js/chrome.js:499
msgid "Restored"
msgstr ""
msgstr "Atkurta"
#. openerp-web
#: addons/web/static/src/js/chrome.js:499
@ -253,7 +253,7 @@ msgstr ""
#: addons/web/static/src/js/formats.js:139
#, python-format
msgid "(%d records)"
msgstr ""
msgstr "(%d įrašai(-ų))"
#. openerp-web
#: addons/web/static/src/js/formats.js:325
@ -265,7 +265,7 @@ msgstr "Atsisiųsti"
#: addons/web/static/src/js/formats.js:330
#, python-format
msgid "Download \"%s\""
msgstr ""
msgstr "Parsisiųsti \"%s\""
#. openerp-web
#: addons/web/static/src/js/search.js:437
@ -527,12 +527,12 @@ msgstr "Forma"
#: addons/web/static/src/xml/base.xml:763
#: addons/web/static/src/xml/base.xml:1714
msgid "Delete"
msgstr ""
msgstr "Ištrinti"
#. openerp-web
#: addons/web/static/src/xml/base.xml:762
msgid "Duplicate"
msgstr ""
msgstr "Sukurti kopiją"
#. openerp-web
#: addons/web/static/src/js/view_form.js:133
@ -589,12 +589,12 @@ msgstr ""
#: addons/web/static/src/js/view_form.js:2238
#, python-format
msgid "<em>   Create \"<strong>%s</strong>\"</em>"
msgstr ""
msgstr "<em>   Sukurti \"<strong>%s</strong>\"</em>"
#. openerp-web
#: addons/web/static/src/js/view_form.js:2244
msgid "<em>   Create and Edit...</em>"
msgstr ""
msgstr "<em>   Sukurti ir redaguoti...</em>"
#. openerp-web
#: addons/web/static/src/js/view_form.js:2277
@ -606,7 +606,7 @@ msgstr "Paieška: "
#: addons/web/static/src/js/view_form.js:2277
#: addons/web/static/src/js/view_form.js:2765
msgid "Create: "
msgstr ""
msgstr "Sukurti "
#. openerp-web
#: addons/web/static/src/js/view_form.js:2062
@ -668,7 +668,7 @@ msgstr "Grupė"
#. openerp-web
#: addons/web/static/src/js/view_list.js:549
msgid "Do you really want to remove these records?"
msgstr ""
msgstr "Ar tikrai norite ištrinti šiuos įrašus?"
#. openerp-web
#: addons/web/static/src/js/views.js:925
@ -813,25 +813,25 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:297
msgid "Invalid username or password"
msgstr ""
msgstr "Neteisingas naudotojo vardas arba slaptažodis"
#. openerp-web
#: addons/web/static/src/xml/base.xml:116
#: addons/web/static/src/xml/base.xml:150
#: addons/web/static/src/xml/base.xml:301
msgid "Database:"
msgstr ""
msgstr "Duomenų bazė:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:306
msgid "Username"
msgstr ""
msgstr "Naudotojas"
#. openerp-web
#: addons/web/static/src/xml/base.xml:308
#: addons/web/static/src/xml/base.xml:331
msgid "Password"
msgstr ""
msgstr "Slaptažodis"
#. openerp-web
#: addons/web/static/src/xml/base.xml:310
@ -870,17 +870,17 @@ msgstr ""
#: addons/web/static/src/xml/base.xml:195
#: addons/web/static/src/xml/base.xml:330
msgid "Restore"
msgstr ""
msgstr "Atkurti"
#. openerp-web
#: addons/web/static/src/xml/base.xml:332
msgid "Back to Login"
msgstr ""
msgstr "Grįžti į prisijungimo langą"
#. openerp-web
#: addons/web/static/src/xml/base.xml:61
msgid "CREATE DATABASE"
msgstr ""
msgstr "SUKURTI DUOMENŲ BAZĘ"
#. openerp-web
#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211
@ -922,7 +922,7 @@ msgstr ""
#: addons/web/static/src/xml/base.xml:162
#: addons/web/static/src/xml/base.xml:187
msgid "Master Password:"
msgstr ""
msgstr "Pagrindinis slaptažodis"
#. openerp-web
#: addons/web/static/src/xml/base.xml:143
@ -932,7 +932,7 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:175
msgid "RESTORE DATABASE"
msgstr ""
msgstr "ATKURTI DUOMENŲ BAZĘ"
#. openerp-web
#: addons/web/static/src/xml/base.xml:182
@ -1012,17 +1012,17 @@ msgstr "OpenERP.com"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1720
msgid "Old Password:"
msgstr ""
msgstr "Esamas slaptažodis:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1725
msgid "New Password:"
msgstr ""
msgstr "Naujas slaptažodis:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1730
msgid "Confirm Password:"
msgstr ""
msgstr "Patvirtinti slaptažodį:"
#. openerp-web
#: addons/web/static/src/xml/base.xml:390
@ -1254,7 +1254,7 @@ msgstr ""
#: addons/web/static/src/xml/base.xml:1222
#: addons/web/static/src/xml/base.xml:1279
msgid "Clear"
msgstr ""
msgstr "Išvalyti"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1179
@ -1408,12 +1408,12 @@ msgstr ""
#. openerp-web
#: addons/web/static/src/xml/base.xml:1509
msgid "Save & New"
msgstr ""
msgstr "Išsaugoti ir sukurti naują"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1510
msgid "Save & Close"
msgstr ""
msgstr "Išsaugoti ir uždaryti"
#. openerp-web
#: addons/web/static/src/xml/base.xml:1617
@ -1572,9 +1572,6 @@ msgstr ""
#~ msgid "Advanced Filters"
#~ msgstr "Išplėstiniai filtrai"
#~ msgid "Other Options"
#~ msgstr "Kiti nustatymai"
#~ msgid "OK"
#~ msgstr "Gerai"
@ -1602,3 +1599,22 @@ msgstr ""
#~ msgid "Reports"
#~ msgstr "Ataskaitos"
#~ msgid "Other Options"
#~ msgstr "Kitos parinktys"
#~ msgid "LOGOUT"
#~ msgstr "ATSIJUNGTI"
#~ msgid "Create..."
#~ msgstr "Sukurti..."
#~ msgid "Search"
#~ msgstr "Ieškoti"
#~ msgid "Search..."
#~ msgstr "Ieškoti..."
#, python-format
#~ msgid "[%(first_record)d to %(last_record)d] of %(records_count)d"
#~ msgstr "[%(first_record)d iki %(last_record)d] iš %(records_count)d"

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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-11-02 05:20+0000\n"
"X-Generator: Launchpad (build 16218)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-11-02 05:20+0000\n"
"X-Generator: Launchpad (build 16218)\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-21 05:02+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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-11-06 05:12+0000\n"
"X-Generator: Launchpad (build 16232)\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-21 05:03+0000\n"
"X-Generator: Launchpad (build 16165)\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

@ -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,20 +20,6 @@
font-style: normal;
}
@media print {
.oe_topbar, .oe_leftbar, .oe_loading {
display: none !important;
}
}
.openerp.openerp_webclient_container {
height: 100%;
}
.text-tag .text-button {
height: auto !important;
min-height: 16px;
}
.openerp {
padding: 0;
margin: 0;
@ -46,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;
@ -197,6 +186,10 @@
.openerp .oe_bounce_container {
display: inline-block;
}
.openerp .text-tag .text-button {
height: auto !important;
min-height: 16px;
}
.openerp .ui-tabs {
position: static;
}
@ -675,7 +668,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 {
@ -1152,6 +1145,7 @@
height: 40px;
width: 157px;
margin: 14px 0;
border: 0;
}
.openerp .oe_footer {
position: fixed;
@ -2322,16 +2316,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;
@ -2377,6 +2371,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;
}
@ -2390,6 +2455,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;
@ -2581,7 +2649,7 @@
background-color: #eeeeee;
}
.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
padding: 4px 6px 3px 6px;
padding: 4px 6px 3px;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
color: transparent;
@ -2594,32 +2662,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;
}
@ -2662,6 +2731,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));
@ -2681,6 +2753,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;
@ -2748,6 +2823,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;
@ -2793,6 +2872,9 @@
content: "}";
color: #e0e0e0;
}
.openerp .oe_list_content .oe_list_field_progressbar progress {
width: 100%;
}
.openerp .tree_header {
background-color: #f0f0f0;
border-bottom: 1px solid #cacaca;
@ -2892,78 +2974,6 @@
color: #333333;
}
.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;
}
.kitten-mode-activated {
background-image: url(http://placekitten.com/g/1365/769);
background-size: cover;
@ -3030,8 +3040,8 @@ div.ui-widget-overlay {
.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 {
display: 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;

View File

@ -140,18 +140,6 @@ $sheet-padding: 16px
// }}}
@media print
.oe_topbar, .oe_leftbar, .oe_loading
display: none !important
.openerp.openerp_webclient_container
height: 100%
// jQueryUI css bug fixing
.text-tag .text-button
height: auto !important
min-height: 16px
.openerp
// Global style {{{
padding: 0
@ -161,6 +149,8 @@ $sheet-padding: 16px
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
@ -254,6 +244,11 @@ $sheet-padding: 16px
.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
@ -583,7 +578,7 @@ $sheet-padding: 16px
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
@ -942,6 +937,7 @@ $sheet-padding: 16px
height: 40px
width: 157px
margin: 14px 0
border: 0
.oe_footer
position: fixed
bottom: 0
@ -1851,13 +1847,13 @@ $sheet-padding: 16px
.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
@ -1900,6 +1896,64 @@ $sheet-padding: 16px
@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
@ -1913,6 +1967,8 @@ $sheet-padding: 16px
line-height: 14px
float: right
padding-left: 2px
input
padding-right: 13px
&.ui-autocomplete
li.oe_m2o_dropdown_option a
font-style: italic
@ -2044,10 +2100,8 @@ $sheet-padding: 16px
background-color: #eee
$row-height: 27px
.oe_list_editable
.oe_list_content
td.oe_list_field_cell
padding: 4px 6px 3px 6px
.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)
*
@ -2057,26 +2111,26 @@ $sheet-padding: 16px
.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
// }}}
@ -2120,6 +2174,10 @@ $sheet-padding: 16px
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)
@ -2131,6 +2189,8 @@ $sheet-padding: 16px
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
@ -2180,6 +2240,8 @@ $sheet-padding: 16px
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)
@ -2207,6 +2269,8 @@ $sheet-padding: 16px
.oe_list_handle
@include text-to-entypo-icon("}",#E0E0E0,18px)
margin-right: 7px
.oe_list_field_progressbar progress
width: 100%
// }}}
// Tree view {{{
.tree_header
@ -2292,67 +2356,6 @@ $sheet-padding: 16px
float: right
color: #333
// }}}
.openerp
.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
// Kitten Mode {{{
.kitten-mode-activated
background-image: url(http://placekitten.com/g/1365/769)
@ -2362,11 +2365,13 @@ $sheet-padding: 16px
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
@ -2393,6 +2398,7 @@ div.ui-widget-overlay
.ui-corner-all
@include radius(3px)
// }}}
.resolution table
width: 100%
@ -2402,8 +2408,9 @@ div.ui-widget-overlay
@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
display: none
.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
@ -2429,5 +2436,7 @@ div.ui-widget-overlay
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");
@ -394,18 +413,18 @@ 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.db_list;
self.db_list = result;
},
function (_, ev) {
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.lang_list;
});
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;
@ -426,6 +445,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label");
self.$el.find("label").addClass("oe_form_label");
self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
self.$el.find("form[name=duplicate_db_form]").validate({ submitHandler: self.do_duplicate });
self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
@ -494,7 +514,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',
@ -511,6 +531,18 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.do_action(client_action);
});
},
do_duplicate: function(form) {
var self = this;
var fields = $(form).serializeArray();
self.rpc("/web/database/duplicate", {'fields': fields}).then(function(result) {
if (result.error) {
self.display_error(result);
return;
}
self.do_notify("Duplicating database", "The database has been duplicated.");
self.start();
});
},
do_drop: function(form) {
var self = this;
var $form = $(form),
@ -520,7 +552,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;
@ -583,7 +615,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;
@ -611,6 +643,9 @@ instance.web.Login = instance.web.Widget.extend({
this.selected_db = null;
this.selected_login = null;
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);
@ -641,7 +676,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");
@ -681,7 +716,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);
@ -712,9 +747,35 @@ instance.web.Login = instance.web.Widget.extend({
});
instance.web.client_actions.add("login", "instance.web.Login");
/**
* 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.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 has an entry 'menu_id', it opens the given menu entry.
* 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 || {};
@ -730,8 +791,8 @@ instance.web.Reload = function(parent, action) {
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");
@ -741,7 +802,7 @@ instance.web.client_actions.add("reload", "instance.web.Reload");
*/
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");
@ -749,11 +810,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) {
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({
@ -764,7 +824,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;
@ -803,7 +863,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);
});
},
@ -973,18 +1033,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');
@ -1000,7 +1060,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();
@ -1020,9 +1080,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();
});
@ -1170,8 +1231,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')); });
},
@ -1186,7 +1247,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();
@ -1199,7 +1260,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);
@ -1213,10 +1274,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 {
@ -1232,11 +1293,12 @@ 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;
@ -1282,9 +1344,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.
@ -809,12 +775,12 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
return false;
},
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var def = $.Deferred().done(success).fail(error);
var self = this;
instance.session.rpc(url, data).then(function() {
instance.session.rpc(url, data).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);
},
@ -1287,7 +1252,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
};
var deferred = $.Deferred();
this.trigger('request', url, payload);
var request = this.rpc_function(url, payload).then(
var request = this.rpc_function(url, payload).done(
function (response, textStatus, jqXHR) {
self.trigger('response', response);
if (!response.error) {
@ -1300,7 +1265,8 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
} else {
deferred.reject(response.error, $.Event());
}
},
}
).fail(
function(jqXHR, textStatus, errorThrown) {
self.trigger('response_failed', jqXHR);
var error = {
@ -1310,7 +1276,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 +1309,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 +1335,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 +1362,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) {
@ -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);

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,7 +132,7 @@ 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 || {};
@ -358,17 +358,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;
@ -443,7 +442,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');
});
},
@ -456,7 +455,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];
});
@ -493,7 +492,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
*/
write: function (id, data, options) {
options = options || {};
return this._model.call('write', [[id], data], {context: this.get_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
@ -501,7 +500,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.get_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
@ -607,7 +606,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
model: this.model,
ids: ids,
context: this.get_context(options.context),
}).pipe(function (results) {
}).then(function (results) {
return results;
});
},
@ -682,9 +681,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');
});
},
@ -693,7 +692,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;
@ -722,10 +721,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,
@ -774,7 +773,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);
@ -836,7 +835,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;});
@ -991,14 +990,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) {
@ -961,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) {
@ -1439,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) {
@ -1462,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]);
})
@ -1490,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')) {
@ -1509,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;
}
@ -1576,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
@ -1656,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');
@ -1699,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
@ -1716,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;}));
}
@ -1741,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) {
@ -1757,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);
},
@ -1767,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

View File

@ -503,12 +503,17 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
return this.reload_content();
},
reload_record: function (record) {
var self = this;
return this.dataset.read_ids(
[record.get('id')],
_.pluck(_(this.columns).filter(function (r) {
return r.tag === 'field';
}), 'name')
).then(function (records) {
).done(function (records) {
if (!records[0]) {
self.records.remove(record);
return;
}
_(records[0]).each(function (value, key) {
record.set(key, value, {silent: true});
});
@ -553,7 +558,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.no_leaf = !!context['group_by_no_leaf'];
this.grouped = !!group_by;
return this.load_view(context).pipe(
return this.load_view(context).then(
this.proxy('reload_content'));
},
/**
@ -566,7 +571,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
return;
}
var self = this;
return $.when(this.dataset.unlink(ids)).then(function () {
return $.when(this.dataset.unlink(ids)).done(function () {
_(ids).each(function (id) {
self.records.remove(self.records.get(id));
});
@ -970,13 +975,13 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// set) a human-readable version. m2o does not have this issue
// because the non-human-readable is just a number, where the
// human-readable version is a pair
if (value && (ref_match = /([\w\.]+),(\d+)/.exec(value))) {
if (value && (ref_match = /^([\w\.]+),(\d+)$/.exec(value))) {
// reference values are in the shape "$model,$id" (as a
// string), we need to split and name_get this pair in order
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
new instance.web.DataSet(this.view, model).name_get([id]).then(function(names) {
new instance.web.DataSet(this.view, model).name_get([id]).done(function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
@ -992,7 +997,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// and let the various registered events handle refreshing the
// row
new instance.web.DataSet(this.view, column.relation)
.name_get([value]).then(function (names) {
.name_get([value]).done(function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
@ -1000,7 +1005,8 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
} else if (column.type === 'many2many') {
value = record.get(column.id);
// non-resolved (string) m2m values are arrays
if (value instanceof Array && !_.isEmpty(value)) {
if (value instanceof Array && !_.isEmpty(value)
&& !record.get(column.id + '__display')) {
var ids;
// they come in two shapes:
if (value[0] instanceof Array) {
@ -1015,9 +1021,14 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
ids = value;
}
new instance.web.Model(column.relation)
.call('name_get', [ids]).then(function (names) {
record.set(column.id, _(names).pluck(1).join(', '));
})
.call('name_get', [ids]).done(function (names) {
// FIXME: nth horrible hack in this poor listview
record.set(column.id + '__display',
_(names).pluck(1).join(', '));
record.set(column.id, ids);
});
// temp empty value
record.set(column.id, false);
}
}
return column.format(record.toForm().data, {
@ -1306,9 +1317,11 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
process_modifiers: false
});
} catch (e) {
group_label = row_data[group_column.id].value;
group_label = _.str.escapeHTML(row_data[group_column.id].value);
}
$group_column.text(_.str.sprintf("%s (%d)",
// group_label is html-clean (through format or explicit
// escaping if format failed), can inject straight into HTML
$group_column.html(_.str.sprintf("%s (%d)",
group_label, group.length));
if (group.length && group.openable) {
@ -1383,43 +1396,45 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name');
var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
//TODO xmo: investigate why we need to put the setTimeout
$.async_when().then(function() {dataset.read_slice(fields, options).then(function (records) {
// FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
if (self.records.length) {
self.records.reset(null, {silent: true});
}
if (!self.datagroup.openable) {
view.configure_pager(dataset);
} else {
if (dataset.size() == records.length) {
// only one page
self.$row.find('td.oe_list_group_pagination').empty();
} else {
var pages = Math.ceil(dataset.size() / limit);
self.$row
.find('.oe_list_pager_state')
.text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
page: page + 1,
page_count: pages
}))
.end()
.find('button[data-pager-action=previous]')
.css('visibility',
page === 0 ? 'hidden' : '')
.end()
.find('button[data-pager-action=next]')
.css('visibility',
page === pages - 1 ? 'hidden' : '');
$.async_when().done(function() {
dataset.read_slice(fields, options).done(function (records) {
// FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
if (self.records.length) {
self.records.reset(null, {silent: true});
}
if (!self.datagroup.openable) {
view.configure_pager(dataset);
} else {
if (dataset.size() == records.length) {
// only one page
self.$row.find('td.oe_list_group_pagination').empty();
} else {
var pages = Math.ceil(dataset.size() / limit);
self.$row
.find('.oe_list_pager_state')
.text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
page: page + 1,
page_count: pages
}))
.end()
.find('button[data-pager-action=previous]')
.css('visibility',
page === 0 ? 'hidden' : '')
.end()
.find('button[data-pager-action=next]')
.css('visibility',
page === pages - 1 ? 'hidden' : '');
}
}
}
self.records.add(records, {silent: true});
list.render();
d.resolve(list);
if (_.isEmpty(records)) {
view.no_result();
}
});});
self.records.add(records, {silent: true});
list.render();
d.resolve(list);
if (_.isEmpty(records)) {
view.no_result();
}
});
});
return d.promise();
},
setup_resequence_rows: function (list, dataset) {
@ -1475,7 +1490,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
// Accounting > Taxes > Taxes, child tax accounts)
// when synchronous (without setTimeout)
(function (dataset, id, seq) {
$.async_when().then(function () {
$.async_when().done(function () {
var attrs = {};
attrs[seqname] = seq;
dataset.write(id, attrs);
@ -1500,7 +1515,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
self.render_dataset(dataset).then(function (list) {
self.render_dataset(dataset).done(function (list) {
self.children[null] = list;
self.elements =
[list.$current.replaceAll($el)[0]];
@ -1547,9 +1562,8 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
});
var DataGroup = instance.web.CallbackEnabled.extend({
var DataGroup = instance.web.Class.extend({
init: function(parent, model, domain, context, group_by, level) {
this._super(parent, null);
this.model = new instance.web.Model(model, context, domain);
this.group_by = group_by;
this.context = context;
@ -1560,7 +1574,7 @@ var DataGroup = instance.web.CallbackEnabled.extend({
list: function (fields, ifGroups, ifRecords) {
var self = this;
var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
$.when(query).then(function (querygroups) {
$.when(query).done(function (querygroups) {
// leaf node
if (!querygroups) {
var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
@ -2010,6 +2024,8 @@ instance.web.list.columns = new instance.web.Registry({
'field.progressbar': 'instance.web.list.ProgressBar',
'field.handle': 'instance.web.list.Handle',
'button': 'instance.web.list.Button',
'field.many2onebutton': 'instance.web.list.Many2OneButton',
'field.many2many': 'instance.web.list.Many2Many'
});
instance.web.list.columns.for_ = function (id, field, node) {
var description = _.extend({tag: node.tag}, field, node.attrs);
@ -2140,7 +2156,7 @@ instance.web.list.Boolean = instance.web.list.Column.extend({
* @private
*/
_format: function (row_data, options) {
return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
return _.str.sprintf('<input type="checkbox" %s readonly="readonly"/>',
row_data[this.id].value ? 'checked="checked"' : '');
}
});
@ -2152,20 +2168,24 @@ instance.web.list.Binary = instance.web.list.Column.extend({
*/
_format: function (row_data, options) {
var text = _t("Download");
var download_url = _.str.sprintf(
'/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
instance.session.session_id, options.model, this.id, options.id);
if (this.filename) {
download_url += '&filename_field=' + this.filename;
if (row_data[this.filename]) {
text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
row_data[this.filename].value, {type: 'char'}));
var value = row_data[this.id].value;
var download_url;
if (value && value.substr(0, 10).indexOf(' ') == -1) {
download_url = "data:application/octet-stream;base64," + value;
} else {
download_url = this.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
if (this.filename) {
download_url += '&filename_field=' + this.filename;
}
}
return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
if (this.filename && row_data[this.filename]) {
text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
row_data[this.filename].value, {type: 'char'}));
}
return _.template('<a href="<%-href%>"><%-text%></a> (<%-size%>)', {
text: text,
href: download_url,
size: row_data[this.id].value
size: instance.web.binary_to_binsize(value),
});
}
});
@ -2197,5 +2217,20 @@ instance.web.list.Handle = instance.web.list.Column.extend({
return '<div class="oe_list_handle">';
}
});
instance.web.list.Many2OneButton = instance.web.list.Column.extend({
_format: function (row_data, options) {
this.has_value = !!row_data[this.id].value;
return QWeb.render('Many2OneButton.cell', {'widget': this});
},
});
instance.web.list.Many2Many = instance.web.list.Column.extend({
_format: function (row_data, options) {
if (!_.isEmpty(row_data[this.id].value)) {
// If value, use __display version for printing
row_data[this.id] = row_data[this.id + '__display'];
}
return this._super(row_data, options);
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

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