diff options
| author | Xavier Morel <xmo@openerp.com> | 2012-04-19 10:12:13 +0200 |
|---|---|---|
| committer | Xavier Morel <xmo@openerp.com> | 2012-04-19 10:12:13 +0200 |
| commit | e7ab44929ff4aa3add8d00bab97fc3e879b9b91c (patch) | |
| tree | a600579e5c3c00c473550db8da591b6634fcbe0f /addons | |
| parent | 45c48d684270f9a88af668f99a1d027df57f68d3 (diff) | |
| parent | f5c37eabe94a04ca62d08f46c1cf3a084fa77f19 (diff) | |
[MERGE] from trunk
bzr revid: xmo@openerp.com-20120419081213-m3fhofkgydj6gp1m
Diffstat (limited to 'addons')
149 files changed, 27485 insertions, 18868 deletions
diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 75c3bbd766f..1ca0553c8e2 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -15,13 +15,11 @@ "static/lib/datejs/parser.js", "static/lib/datejs/sugarpak.js", "static/lib/datejs/extras.js", - #"static/lib/jquery/jquery-1.6.4.js", "static/lib/jquery/jquery-1.7.2b1.js", "static/lib/jquery.MD5/jquery.md5.js", "static/lib/jquery.form/jquery.form.js", "static/lib/jquery.validate/jquery.validate.js", "static/lib/jquery.ba-bbq/jquery.ba-bbq.js", - "static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js", "static/lib/jquery.blockUI/jquery.blockUI.js", "static/lib/jquery.superfish/js/hoverIntent.js", "static/lib/jquery.superfish/js/superfish.js", @@ -35,11 +33,26 @@ "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", "static/lib/underscore/underscore.string.js", + "static/lib/backbone/backbone.js", + + "static/lib/visualsearch/lib/js/visualsearch.js", + "static/lib/visualsearch/lib/js/utils/backbone_extensions.js", + "static/lib/visualsearch/lib/js/utils/hotkeys.js", + "static/lib/visualsearch/lib/js/utils/inflector.js", + "static/lib/visualsearch/lib/js/utils/jquery_extensions.js", + "static/lib/visualsearch/lib/js/utils/search_parser.js", + "static/lib/visualsearch/lib/js/models/search_facets.js", + "static/lib/visualsearch/lib/js/models/search_query.js", + "static/lib/visualsearch/lib/js/templates/templates.js", + "static/lib/visualsearch/lib/js/views/search_facet.js", + "static/lib/visualsearch/lib/js/views/search_input.js", + "static/lib/visualsearch/lib/js/views/search_box.js", + "static/lib/labjs/LAB.src.js", "static/lib/py.js/lib/py.js", - "static/lib/novajs/src/nova.js", "static/src/js/boot.js", - "static/src/js/core.js", + "static/src/js/corelib.js", + "static/src/js/coresetup.js", "static/src/js/dates.js", "static/src/js/formats.js", "static/src/js/chrome.js", @@ -49,7 +62,6 @@ "static/src/js/data_import.js", "static/src/js/search.js", "static/src/js/view_form.js", - "static/src/js/view_page.js", "static/src/js/view_list.js", "static/src/js/view_list_editable.js", "static/src/js/view_tree.js", @@ -57,11 +69,14 @@ ], 'css' : [ "static/lib/jquery.superfish/css/superfish.css", - "static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.17.custom.css", + "static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.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", - "static/src/css/base_old.css", + "static/lib/visualsearch/lib/css/reset.css", + "static/lib/visualsearch/lib/css/workspace.css", + "static/lib/visualsearch/lib/css/icons.css", +# "static/src/css/base_old.css", "static/src/css/base.css", "static/src/css/data_export.css", "static/src/css/data_import.css", diff --git a/addons/web/common/nonliterals.py b/addons/web/common/nonliterals.py index 8128b88fd67..4ee6d122f44 100644 --- a/addons/web/common/nonliterals.py +++ b/addons/web/common/nonliterals.py @@ -45,29 +45,20 @@ class NonLiteralEncoder(simplejson.encoder.JSONEncoder): raise TypeError('Could not encode unknown non-literal %s' % object) _ALLOWED_KEYS = frozenset(['__ref', "__id", '__domains', '__debug', - '__contexts', '__eval_context', 'own_values']) + '__contexts', '__eval_context']) def non_literal_decoder(dct): """ Decodes JSON dicts into :class:`Domain` and :class:`Context` based on magic attribute tags. - - Also handles private context section for the domain or section via the - ``own_values`` dict key. """ if '__ref' in dct: for x in dct: if x not in _ALLOWED_KEYS: raise ValueError("'%s' key not allowed in non literal domain/context" % x) if dct['__ref'] == 'domain': - domain = Domain(None, key=dct['__id']) - if 'own_values' in dct: - domain.own = dct['own_values'] - return domain + return Domain(None, key=dct['__id']) elif dct['__ref'] == 'context': - context = Context(None, key=dct['__id']) - if 'own_values' in dct: - context.own = dct['own_values'] - return context + return Context(None, key=dct['__id']) elif dct["__ref"] == "compound_domain": cdomain = CompoundDomain() for el in dct["__domains"]: @@ -111,7 +102,6 @@ class Domain(BaseDomain): "and a domain string") self.session = session - self.own = {} if domain_string: self.key = binascii.hexlify( hashlib.sha256(domain_string).digest()[:SHORT_HASH_BYTES_SIZE]) @@ -131,8 +121,7 @@ class Domain(BaseDomain): evaluated result. """ ctx = self.session.evaluation_context(context) - if self.own: - ctx.update(self.own) + try: return eval(self.get_domain_string(), SuperDict(ctx)) except NameError as e: @@ -159,7 +148,7 @@ class Context(BaseContext): "and a domain string") self.session = session - self.own = {} + if context_string: self.key = binascii.hexlify( hashlib.sha256(context_string).digest()[:SHORT_HASH_BYTES_SIZE]) @@ -179,8 +168,7 @@ class Context(BaseContext): evaluated result. """ ctx = self.session.evaluation_context(context) - if self.own: - ctx.update(self.own) + try: return eval(self.get_context_string(), SuperDict(ctx)) except NameError as e: diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 896ad1a1889..9b4c8335e35 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -79,7 +79,7 @@ def concat_files(file_list, reader=None, intersperse=""): if reader is None: def reader(f): - with open(f) as fp: + with open(f, 'rb') as fp: return fp.read() files_content = [] @@ -261,8 +261,8 @@ class WebClient(openerpweb.Controller): def reader(f): """read the a css file and absolutify all relative uris""" - with open(f) as fp: - data = fp.read() + with open(f, 'rb') as fp: + data = fp.read().decode('utf-8') path = file_map[f] # convert FS path into web path @@ -279,7 +279,7 @@ class WebClient(openerpweb.Controller): r"""url(\1%s/""" % (web_dir,), data, ) - return data + return data.encode('utf-8') content, checksum = concat_files((f[0] for f in files), reader) @@ -802,9 +802,6 @@ def fix_view_modes(action): new view mode ``list`` which is the result of the ``tree`` view_mode in conjunction with the ``form`` view_type. - This method also adds a ``page`` view mode in case there is a ``form`` in - the input action. - TODO: this should go into the doc, some kind of "peculiarities" section :param dict action: an action descriptor @@ -818,8 +815,6 @@ def fix_view_modes(action): if mode == 'form': id_form = id break - if id_form is not None: - action['views'].insert(index + 1, (id_form, 'page')) if action.pop('view_type', 'form') != 'form': return action @@ -875,13 +870,13 @@ class Menu(openerpweb.Controller): context = req.session.eval_context(req.context) Menus = req.session.model('ir.ui.menu') - menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action'], context) + menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context) menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, ''], 'children' : menu_roots} # menus are loaded fully unlike a regular tree view, cause there are a # limited number of items (752 when all 6.1 addons are installed) menu_ids = Menus.search([], 0, False, False, context) - menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action'], context) + menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context) # adds roots at the end of the sequence, so that they will overwrite # equivalent menu items from full menu read when put into id:item # mapping, resulting in children being correctly set on the roots. @@ -1083,6 +1078,7 @@ class View(openerpweb.Controller): arch = fvg['arch'].encode('utf-8') else: arch = fvg['arch'] + fvg['arch_string'] = arch if transform: evaluation_context = session.evaluation_context(context or {}) diff --git a/addons/web/i18n/bs.po b/addons/web/i18n/bs.po new file mode 100644 index 00000000000..e2470a334ec --- /dev/null +++ b/addons/web/i18n/bs.po @@ -0,0 +1,1544 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-13 20:46+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-14 05:04+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:172 +#: addons/web/static/src/js/chrome.js:198 +#: addons/web/static/src/js/chrome.js:414 +#: addons/web/static/src/js/view_form.js:419 +#: addons/web/static/src/js/view_form.js:1233 +#: addons/web/static/src/xml/base.xml:1695 +#: addons/web/static/src/js/view_form.js:424 +#: addons/web/static/src/js/view_form.js:1239 +msgid "Ok" +msgstr "Uredu" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:180 +msgid "Send OpenERP Enterprise Report" +msgstr "Pošalji OpenERP Enterprise Izvještaj" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:194 +msgid "Dont send" +msgstr "Nemoj Poslat" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:256 +#, python-format +msgid "Loading (%d)" +msgstr "Punim (%d)" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:288 +msgid "Invalid database name" +msgstr "Nevažeće ime baze podataka" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:483 +msgid "Backed" +msgstr "Spašeno" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:484 +msgid "Database backed up successfully" +msgstr "Baza podataka uspješno kopirana" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Restored" +msgstr "Obnovljeno" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Database restored successfully" +msgstr "Baza podataka uspješno obnovljena" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:708 +#: addons/web/static/src/xml/base.xml:359 +msgid "About" +msgstr "O programu" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:787 +#: addons/web/static/src/xml/base.xml:356 +msgid "Preferences" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:790 +#: addons/web/static/src/js/search.js:239 +#: addons/web/static/src/js/search.js:288 +#: addons/web/static/src/js/view_editor.js:95 +#: addons/web/static/src/js/view_editor.js:836 +#: addons/web/static/src/js/view_editor.js:962 +#: addons/web/static/src/js/view_form.js:1228 +#: addons/web/static/src/xml/base.xml:738 +#: addons/web/static/src/xml/base.xml:1496 +#: addons/web/static/src/xml/base.xml:1506 +#: addons/web/static/src/xml/base.xml:1515 +#: addons/web/static/src/js/search.js:293 +#: addons/web/static/src/js/view_form.js:1234 +msgid "Cancel" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:791 +msgid "Change password" +msgstr "Promijeni šifru" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:792 +#: addons/web/static/src/js/view_editor.js:73 +#: addons/web/static/src/js/views.js:962 +#: addons/web/static/src/xml/base.xml:737 +#: addons/web/static/src/xml/base.xml:1500 +#: addons/web/static/src/xml/base.xml:1514 +msgid "Save" +msgstr "Sačuvaj" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:811 +#: addons/web/static/src/xml/base.xml:226 +#: addons/web/static/src/xml/base.xml:1729 +msgid "Change Password" +msgstr "Promijeni šifru" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1096 +#: addons/web/static/src/js/chrome.js:1100 +msgid "OpenERP - Unsupported/Community Version" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1131 +#: addons/web/static/src/js/chrome.js:1135 +msgid "Client Error" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:6 +msgid "Export Data" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:19 +#: addons/web/static/src/js/data_import.js:69 +#: addons/web/static/src/js/view_editor.js:49 +#: addons/web/static/src/js/view_editor.js:398 +#: addons/web/static/src/js/view_form.js:692 +#: addons/web/static/src/js/view_form.js:3044 +#: addons/web/static/src/js/views.js:963 +#: addons/web/static/src/js/view_form.js:698 +#: addons/web/static/src/js/view_form.js:3067 +msgid "Close" +msgstr "Zatvori" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:20 +msgid "Export To File" +msgstr "Eksportuj do fajla" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:125 +msgid "Please enter save field list name" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:360 +msgid "Please select fields to save export list..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:373 +msgid "Please select fields to export..." +msgstr "Molimo odaberite mjesto za eksportovanje.." + +#. openerp-web +#: addons/web/static/src/js/data_import.js:34 +msgid "Import Data" +msgstr "Uvezi podatke" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:70 +msgid "Import File" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:105 +msgid "External ID" +msgstr "Externi ID" + +#. openerp-web +#: addons/web/static/src/js/formats.js:300 +#: addons/web/static/src/js/view_page.js:245 +#: addons/web/static/src/js/formats.js:322 +#: addons/web/static/src/js/view_page.js:251 +msgid "Download" +msgstr "Preuzimanje" + +#. openerp-web +#: addons/web/static/src/js/formats.js:305 +#: addons/web/static/src/js/formats.js:327 +#, python-format +msgid "Download \"%s\"" +msgstr "Preuzimanje \"%s\"" + +#. openerp-web +#: addons/web/static/src/js/search.js:191 +msgid "Filter disabled due to invalid syntax" +msgstr "Filter onemogučen zbog ne validne sintakse" + +#. openerp-web +#: addons/web/static/src/js/search.js:237 +msgid "Filter Entry" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:242 +#: addons/web/static/src/js/search.js:291 +#: addons/web/static/src/js/search.js:296 +msgid "OK" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:286 +#: addons/web/static/src/xml/base.xml:1292 +#: addons/web/static/src/js/search.js:291 +msgid "Add to Dashboard" +msgstr "Dodaj u Dashboard" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "Invalid Search" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "triggered from search view" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:503 +#: addons/web/static/src/js/search.js:508 +#, python-format +msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:839 +#: addons/web/static/src/js/search.js:844 +msgid "not a valid integer" +msgstr "ne važeći integer" + +#. openerp-web +#: addons/web/static/src/js/search.js:853 +#: addons/web/static/src/js/search.js:858 +msgid "not a valid number" +msgstr "Ne važeći broj" + +#. openerp-web +#: addons/web/static/src/js/search.js:931 +#: addons/web/static/src/xml/base.xml:968 +#: addons/web/static/src/js/search.js:936 +msgid "Yes" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:932 +#: addons/web/static/src/js/search.js:937 +msgid "No" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1290 +#: addons/web/static/src/js/search.js:1295 +msgid "contains" +msgstr "sadrži" + +#. openerp-web +#: addons/web/static/src/js/search.js:1291 +#: addons/web/static/src/js/search.js:1296 +msgid "doesn't contain" +msgstr "Ne sarži" + +#. openerp-web +#: addons/web/static/src/js/search.js:1292 +#: addons/web/static/src/js/search.js:1306 +#: addons/web/static/src/js/search.js:1325 +#: addons/web/static/src/js/search.js:1344 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +msgid "is equal to" +msgstr "je jednako" + +#. openerp-web +#: addons/web/static/src/js/search.js:1293 +#: addons/web/static/src/js/search.js:1307 +#: addons/web/static/src/js/search.js:1326 +#: addons/web/static/src/js/search.js:1345 +#: addons/web/static/src/js/search.js:1366 +#: addons/web/static/src/js/search.js:1298 +#: addons/web/static/src/js/search.js:1312 +#: addons/web/static/src/js/search.js:1331 +#: addons/web/static/src/js/search.js:1350 +#: addons/web/static/src/js/search.js:1371 +msgid "is not equal to" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1294 +#: addons/web/static/src/js/search.js:1308 +#: addons/web/static/src/js/search.js:1327 +#: addons/web/static/src/js/search.js:1346 +#: addons/web/static/src/js/search.js:1367 +#: addons/web/static/src/js/search.js:1299 +#: addons/web/static/src/js/search.js:1313 +#: addons/web/static/src/js/search.js:1332 +#: addons/web/static/src/js/search.js:1351 +#: addons/web/static/src/js/search.js:1372 +msgid "greater than" +msgstr "veće od" + +#. openerp-web +#: addons/web/static/src/js/search.js:1295 +#: addons/web/static/src/js/search.js:1309 +#: addons/web/static/src/js/search.js:1328 +#: addons/web/static/src/js/search.js:1347 +#: addons/web/static/src/js/search.js:1368 +#: addons/web/static/src/js/search.js:1300 +#: addons/web/static/src/js/search.js:1314 +#: addons/web/static/src/js/search.js:1333 +#: addons/web/static/src/js/search.js:1352 +#: addons/web/static/src/js/search.js:1373 +msgid "less than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1296 +#: addons/web/static/src/js/search.js:1310 +#: addons/web/static/src/js/search.js:1329 +#: addons/web/static/src/js/search.js:1348 +#: addons/web/static/src/js/search.js:1369 +#: addons/web/static/src/js/search.js:1301 +#: addons/web/static/src/js/search.js:1315 +#: addons/web/static/src/js/search.js:1334 +#: addons/web/static/src/js/search.js:1353 +#: addons/web/static/src/js/search.js:1374 +msgid "greater or equal than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +#: addons/web/static/src/js/search.js:1302 +#: addons/web/static/src/js/search.js:1316 +#: addons/web/static/src/js/search.js:1335 +#: addons/web/static/src/js/search.js:1354 +#: addons/web/static/src/js/search.js:1375 +msgid "less or equal than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1360 +#: addons/web/static/src/js/search.js:1383 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1388 +msgid "is" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1384 +#: addons/web/static/src/js/search.js:1389 +msgid "is not" +msgstr "nije" + +#. openerp-web +#: addons/web/static/src/js/search.js:1396 +#: addons/web/static/src/js/search.js:1401 +msgid "is true" +msgstr "je istinito" + +#. openerp-web +#: addons/web/static/src/js/search.js:1397 +#: addons/web/static/src/js/search.js:1402 +msgid "is false" +msgstr "je pogrešno" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:20 +#, python-format +msgid "Manage Views (%s)" +msgstr "Upravljanje pregledom (%s)" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:46 +#: addons/web/static/src/js/view_list.js:17 +#: addons/web/static/src/xml/base.xml:100 +#: addons/web/static/src/xml/base.xml:327 +#: addons/web/static/src/xml/base.xml:756 +msgid "Create" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:47 +#: addons/web/static/src/xml/base.xml:483 +#: addons/web/static/src/xml/base.xml:755 +msgid "Edit" +msgstr "Uređivanje" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:48 +#: addons/web/static/src/xml/base.xml:1647 +msgid "Remove" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:71 +#, python-format +msgid "Create a view (%s)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:168 +msgid "Do you really want to remove this view?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:364 +#, python-format +msgid "View Editor %d - %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:367 +msgid "Inherited View" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:371 +msgid "Do you really wants to create an inherited view here?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:381 +msgid "Preview" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:501 +msgid "Do you really want to remove this node?" +msgstr "Da li sigurno želite uklonuti ovaj čvor?" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:815 +#: addons/web/static/src/js/view_editor.js:939 +msgid "Properties" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:818 +#: addons/web/static/src/js/view_editor.js:942 +msgid "Update" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:16 +msgid "Form" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:121 +#: addons/web/static/src/js/views.js:803 +msgid "Customize" +msgstr "Podesi" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:123 +#: addons/web/static/src/js/view_form.js:686 +#: addons/web/static/src/js/view_form.js:692 +msgid "Set Default" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:469 +#: addons/web/static/src/js/view_form.js:475 +msgid "" +"Warning, the record has been modified, your changes will be discarded." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:693 +#: addons/web/static/src/js/view_form.js:699 +msgid "Save default" +msgstr "Snimi zadano" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:754 +#: addons/web/static/src/js/view_form.js:760 +msgid "Attachments" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:792 +#: addons/web/static/src/js/view_form.js:798 +#, python-format +msgid "Do you really want to delete the attachment %s?" +msgstr "Da li sigurno želite izbrisati dodatak %s?" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:822 +#: addons/web/static/src/js/view_form.js:828 +#, python-format +msgid "Unknown operator %s in domain %s" +msgstr "Nepoznat operator %s na domeni %s" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:830 +#: addons/web/static/src/js/view_form.js:836 +#, python-format +msgid "Unknown field %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:868 +#: addons/web/static/src/js/view_form.js:874 +#, python-format +msgid "Unsupported operator %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1225 +#: addons/web/static/src/js/view_form.js:1231 +msgid "Confirm" +msgstr "Portvrdi" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1921 +#: addons/web/static/src/js/view_form.js:2578 +#: addons/web/static/src/js/view_form.js:2741 +#: addons/web/static/src/js/view_form.js:1933 +#: addons/web/static/src/js/view_form.js:2590 +#: addons/web/static/src/js/view_form.js:2760 +msgid "Open: " +msgstr "Otvori: " + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2049 +#: addons/web/static/src/js/view_form.js:2061 +msgid "<em> Search More...</em>" +msgstr "<em> Traži Više...</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2062 +#: addons/web/static/src/js/view_form.js:2074 +#, python-format +msgid "<em> Create \"<strong>%s</strong>\"</em>" +msgstr "<em> Kreiraj\"<strong>%s</strong>\"</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2068 +#: addons/web/static/src/js/view_form.js:2080 +msgid "<em> Create and Edit...</em>" +msgstr "<em> Kreiraj i Uredi...</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/views.js:675 +#: addons/web/static/src/js/view_form.js:2113 +msgid "Search: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/view_form.js:2550 +#: addons/web/static/src/js/view_form.js:2113 +#: addons/web/static/src/js/view_form.js:2562 +msgid "Create: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2661 +#: addons/web/static/src/xml/base.xml:750 +#: addons/web/static/src/xml/base.xml:772 +#: addons/web/static/src/xml/base.xml:1646 +#: addons/web/static/src/js/view_form.js:2680 +msgid "Add" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2721 +#: addons/web/static/src/js/view_form.js:2740 +msgid "Add: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:8 +msgid "List" +msgstr "Lista" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:269 +msgid "Unlimited" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:305 +#: addons/web/static/src/js/view_list.js:309 +#, python-format +msgid "[%(first_record)d to %(last_record)d] of %(records_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:524 +#: addons/web/static/src/js/view_list.js:528 +msgid "Do you really want to remove these records?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1230 +#: addons/web/static/src/js/view_list.js:1232 +msgid "Undefined" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1327 +#: addons/web/static/src/js/view_list.js:1331 +#, python-format +msgid "%(page)d/%(page_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:8 +msgid "Page" +msgstr "Stranica" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:52 +msgid "Do you really want to delete this record?" +msgstr "Jeste li sigurni da želite izbrisati ovaj zapis?" + +#. openerp-web +#: addons/web/static/src/js/view_tree.js:11 +msgid "Tree" +msgstr "Stablo" + +#. openerp-web +#: addons/web/static/src/js/views.js:565 +#: addons/web/static/src/xml/base.xml:480 +msgid "Fields View Get" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:573 +#, python-format +msgid "View Log (%s)" +msgstr "Pogledaj Log (%s)" + +#. openerp-web +#: addons/web/static/src/js/views.js:600 +#, python-format +msgid "Model %s fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:610 +#: addons/web/static/src/xml/base.xml:482 +msgid "Manage Views" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:611 +msgid "Could not find current view declaration" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:805 +msgid "Translate" +msgstr "Prevedi" + +#. openerp-web +#: addons/web/static/src/js/views.js:807 +msgid "Technical translation" +msgstr "Tehnički prevod" + +#. openerp-web +#: addons/web/static/src/js/views.js:811 +msgid "Other Options" +msgstr "Ostale opcije" + +#. openerp-web +#: addons/web/static/src/js/views.js:814 +#: addons/web/static/src/xml/base.xml:1736 +msgid "Import" +msgstr "Uvezi" + +#. openerp-web +#: addons/web/static/src/js/views.js:817 +#: addons/web/static/src/xml/base.xml:1606 +msgid "Export" +msgstr "Izvoz" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Reports" +msgstr "Izvještaji" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Actions" +msgstr "Akcije" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Links" +msgstr "Linkovi" + +#. openerp-web +#: addons/web/static/src/js/views.js:919 +msgid "You must choose at least one record." +msgstr "Morate izabrati barem jedan zapis" + +#. openerp-web +#: addons/web/static/src/js/views.js:920 +msgid "Warning" +msgstr "Upozorenje" + +#. openerp-web +#: addons/web/static/src/js/views.js:957 +msgid "Translations" +msgstr "Prevodi" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +msgid "Powered by" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:1813 +msgid "OpenERP" +msgstr "OpenERP" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:52 +msgid "Loading..." +msgstr "Učitavam..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:61 +msgid "CREATE DATABASE" +msgstr "Kreiraj Bazu podataka" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:68 +#: addons/web/static/src/xml/base.xml:211 +msgid "Master password:" +msgstr "Glavni Password:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:72 +#: addons/web/static/src/xml/base.xml:191 +msgid "New database name:" +msgstr "Naziv nove baze podataka:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:77 +msgid "Load Demonstration data:" +msgstr "Učitavam Demonstrativne podatke" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:81 +msgid "Default language:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:91 +msgid "Admin password:" +msgstr "Administratorov pasword:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:95 +msgid "Confirm password:" +msgstr "Potvrdi password:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:109 +msgid "DROP DATABASE" +msgstr "Izbaci Bazu podataka" + +#. 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 "Baza podataka:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:128 +#: addons/web/static/src/xml/base.xml:162 +#: addons/web/static/src/xml/base.xml:187 +msgid "Master Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:132 +#: addons/web/static/src/xml/base.xml:328 +msgid "Drop" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:143 +msgid "BACKUP DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:166 +#: addons/web/static/src/xml/base.xml:329 +msgid "Backup" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:175 +msgid "RESTORE DATABASE" +msgstr "Povrati Bazu podataka" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:182 +msgid "File:" +msgstr "Datoteka:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:195 +#: addons/web/static/src/xml/base.xml:330 +msgid "Restore" +msgstr "Vrati" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:204 +msgid "CHANGE MASTER PASSWORD" +msgstr "Promijeni glavni password" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:216 +msgid "New master password:" +msgstr "Novi glavni password" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:221 +msgid "Confirm new master password:" +msgstr "Potvrdi glavni password" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "" +"Your version of OpenERP is unsupported. Support & maintenance services are " +"available here:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "OpenERP Entreprise" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:256 +msgid "OpenERP Enterprise Contract." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:257 +msgid "Your report will be sent to the OpenERP Enterprise team." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:259 +msgid "Summary:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:263 +msgid "Description:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:267 +msgid "What you did:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:297 +msgid "Invalid username or password" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:306 +msgid "Username" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:308 +#: addons/web/static/src/xml/base.xml:331 +msgid "Password" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:310 +msgid "Log in" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:314 +msgid "Manage Databases" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:332 +msgid "Back to Login" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:353 +msgid "Home" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:363 +msgid "LOGOUT" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:388 +msgid "Fold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:389 +msgid "Unfold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:454 +msgid "Hide this tip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:455 +msgid "Disable all tips" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:463 +msgid "Add / Remove Shortcut..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:471 +msgid "More…" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:477 +msgid "Debug View#" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:478 +msgid "View Log (perm_read)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:479 +msgid "View Fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:483 +msgid "View" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:484 +msgid "Edit SearchView" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:485 +msgid "Edit Action" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:486 +msgid "Edit Workflow" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:491 +msgid "ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:494 +msgid "XML ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:497 +msgid "Creation User:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:500 +msgid "Creation Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:503 +msgid "Latest Modification by:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:506 +msgid "Latest Modification Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:542 +msgid "Field" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:632 +#: addons/web/static/src/xml/base.xml:758 +#: addons/web/static/src/xml/base.xml:1708 +msgid "Delete" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:757 +msgid "Duplicate" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:775 +msgid "Add attachment" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:801 +msgid "Default:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:818 +msgid "Condition:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:837 +msgid "Only you" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:844 +msgid "All users" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:851 +msgid "Unhandled widget" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:900 +msgid "Notebook Page \"" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:905 +#: addons/web/static/src/xml/base.xml:964 +msgid "Modifiers:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:931 +msgid "(nolabel)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:936 +msgid "Field:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:940 +msgid "Object:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:944 +msgid "Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:948 +msgid "Widget:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:952 +msgid "Size:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:956 +msgid "Context:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:960 +msgid "Domain:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:968 +msgid "Change default:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:972 +msgid "On change:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:976 +msgid "Relation:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:980 +msgid "Selection:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1020 +msgid "Send an e-mail with your default e-mail client" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1034 +msgid "Open this resource" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1056 +msgid "Select date" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1090 +msgid "Open..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1091 +msgid "Create..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1092 +msgid "Search..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1095 +msgid "..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1155 +#: addons/web/static/src/xml/base.xml:1198 +msgid "Set Image" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1163 +#: addons/web/static/src/xml/base.xml:1213 +#: addons/web/static/src/xml/base.xml:1215 +#: addons/web/static/src/xml/base.xml:1272 +msgid "Clear" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1172 +#: addons/web/static/src/xml/base.xml:1223 +msgid "Uploading ..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1200 +#: addons/web/static/src/xml/base.xml:1495 +msgid "Select" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1207 +#: addons/web/static/src/xml/base.xml:1209 +msgid "Save As" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1238 +msgid "Button" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1241 +msgid "(no string)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1248 +msgid "Special:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1253 +msgid "Button Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1257 +msgid "Method:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1261 +msgid "Action ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1271 +msgid "Search" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1279 +msgid "Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1280 +msgid "-- Filters --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1289 +msgid "-- Actions --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1290 +msgid "Add Advanced Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1291 +msgid "Save Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1293 +msgid "Manage Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1298 +msgid "Filter Name:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1300 +msgid "(Any existing filter with the same name will be replaced)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1305 +msgid "Select Dashboard to add this filter to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1309 +msgid "Title of new Dashboard item:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1416 +msgid "Advanced Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1426 +msgid "Any of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1427 +msgid "All the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1428 +msgid "None of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1435 +msgid "Add condition" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1436 +msgid "and" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1503 +msgid "Save & New" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1504 +msgid "Save & Close" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1611 +msgid "" +"This wizard will export all data that matches the current search criteria to " +"a CSV file.\n" +" You can export all data or only the fields that can be " +"reimported after modification." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1618 +msgid "Export Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1620 +msgid "Import Compatible Export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1621 +msgid "Export all Data" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1624 +msgid "Export Formats" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1630 +msgid "Available fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1632 +msgid "Fields to export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1634 +msgid "Save fields list" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1648 +msgid "Remove All" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1660 +msgid "Name" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1693 +msgid "Save as:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1700 +msgid "Saved exports:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1714 +msgid "Old Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1719 +msgid "New Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1724 +msgid "Confirm Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1742 +msgid "1. Import a .CSV file" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1743 +msgid "" +"Select a .CSV file to import. If you need a sample of file to import,\n" +" you should use the export tool with the \"Import Compatible\" option." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1747 +msgid "CSV File:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1750 +msgid "2. Check your file format" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1753 +msgid "Import Options" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1757 +msgid "Does your file have titles?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1763 +msgid "Separator:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1765 +msgid "Delimiter:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1769 +msgid "Encoding:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1772 +msgid "UTF-8" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1773 +msgid "Latin 1" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "Lines to skip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "" +"For use if CSV files have titles on multiple lines, skips more than a single " +"line during import" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1803 +msgid "The import failed due to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1805 +msgid "Here is a preview of the file we could not import:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1812 +msgid "Activate the developper mode" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1814 +msgid "Version" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1815 +msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1816 +msgid "OpenERP is a trademark of the" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1817 +msgid "OpenERP SA Company" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1819 +msgid "Licenced under the terms of" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1820 +msgid "GNU Affero General Public License" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1822 +msgid "For more information visit" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1823 +msgid "OpenERP.com" +msgstr "" diff --git a/addons/web/i18n/da.po b/addons/web/i18n/da.po index 59344532fb3..0c8f9615955 100644 --- a/addons/web/i18n/da.po +++ b/addons/web/i18n/da.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-14 15:27+0100\n" -"PO-Revision-Date: 2011-11-08 21:58+0000\n" +"PO-Revision-Date: 2012-04-06 12:13+0000\n" "Last-Translator: Jonas Mortensen <Unknown>\n" "Language-Team: Danish <da@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n" -"X-Generator: Launchpad (build 14781)\n" +"X-Launchpad-Export-Date: 2012-04-07 05:03+0000\n" +"X-Generator: Launchpad (build 15060)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:172 @@ -24,6 +24,8 @@ msgstr "" #: addons/web/static/src/js/view_form.js:419 #: addons/web/static/src/js/view_form.js:1233 #: addons/web/static/src/xml/base.xml:1695 +#: addons/web/static/src/js/view_form.js:424 +#: addons/web/static/src/js/view_form.js:1239 msgid "Ok" msgstr "Ok" @@ -35,7 +37,7 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/chrome.js:194 msgid "Dont send" -msgstr "" +msgstr "Send ikke" #. openerp-web #: addons/web/static/src/js/chrome.js:256 @@ -56,29 +58,29 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/chrome.js:484 msgid "Database backed up successfully" -msgstr "" +msgstr "Backup af database blev afsluttet med succes" #. openerp-web #: addons/web/static/src/js/chrome.js:527 msgid "Restored" -msgstr "" +msgstr "Gendannet" #. openerp-web #: addons/web/static/src/js/chrome.js:527 msgid "Database restored successfully" -msgstr "" +msgstr "Databasen blev gendannet korrekt" #. openerp-web #: addons/web/static/src/js/chrome.js:708 #: addons/web/static/src/xml/base.xml:359 msgid "About" -msgstr "" +msgstr "Om" #. openerp-web #: addons/web/static/src/js/chrome.js:787 #: addons/web/static/src/xml/base.xml:356 msgid "Preferences" -msgstr "" +msgstr "Egenskaber" #. openerp-web #: addons/web/static/src/js/chrome.js:790 @@ -92,18 +94,21 @@ msgstr "" #: addons/web/static/src/xml/base.xml:1496 #: addons/web/static/src/xml/base.xml:1506 #: addons/web/static/src/xml/base.xml:1515 +#: addons/web/static/src/js/search.js:293 +#: addons/web/static/src/js/view_form.js:1234 msgid "Cancel" msgstr "Annullér" #. openerp-web #: addons/web/static/src/js/chrome.js:791 msgid "Change password" -msgstr "" +msgstr "Skift adgangskode" #. openerp-web #: addons/web/static/src/js/chrome.js:792 #: addons/web/static/src/js/view_editor.js:73 -#: addons/web/static/src/js/views.js:962 addons/web/static/src/xml/base.xml:737 +#: addons/web/static/src/js/views.js:962 +#: addons/web/static/src/xml/base.xml:737 #: addons/web/static/src/xml/base.xml:1500 #: addons/web/static/src/xml/base.xml:1514 msgid "Save" @@ -114,22 +119,24 @@ msgstr "Gem" #: addons/web/static/src/xml/base.xml:226 #: addons/web/static/src/xml/base.xml:1729 msgid "Change Password" -msgstr "" +msgstr "Skift adgangskode" #. openerp-web #: addons/web/static/src/js/chrome.js:1096 +#: addons/web/static/src/js/chrome.js:1100 msgid "OpenERP - Unsupported/Community Version" -msgstr "" +msgstr "OpenERP - Ikke understøttet af officiel support/Community Udgave" #. openerp-web #: addons/web/static/src/js/chrome.js:1131 +#: addons/web/static/src/js/chrome.js:1135 msgid "Client Error" msgstr "" #. openerp-web #: addons/web/static/src/js/data_export.js:6 msgid "Export Data" -msgstr "" +msgstr "Eksporter Data" #. openerp-web #: addons/web/static/src/js/data_export.js:19 @@ -139,13 +146,15 @@ msgstr "" #: addons/web/static/src/js/view_form.js:692 #: addons/web/static/src/js/view_form.js:3044 #: addons/web/static/src/js/views.js:963 +#: addons/web/static/src/js/view_form.js:698 +#: addons/web/static/src/js/view_form.js:3067 msgid "Close" msgstr "Luk" #. openerp-web #: addons/web/static/src/js/data_export.js:20 msgid "Export To File" -msgstr "" +msgstr "Eksporter Til Fil" #. openerp-web #: addons/web/static/src/js/data_export.js:125 @@ -155,39 +164,42 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/data_export.js:360 msgid "Please select fields to save export list..." -msgstr "" +msgstr "Vælg venligst mulighed for at gemme eksporteringsliste..." #. openerp-web #: addons/web/static/src/js/data_export.js:373 msgid "Please select fields to export..." -msgstr "" +msgstr "Vælg venligst mulighed for at eksportere" #. openerp-web #: addons/web/static/src/js/data_import.js:34 msgid "Import Data" -msgstr "" +msgstr "Importer data" #. openerp-web #: addons/web/static/src/js/data_import.js:70 msgid "Import File" -msgstr "" +msgstr "Importér fil" #. openerp-web #: addons/web/static/src/js/data_import.js:105 msgid "External ID" -msgstr "" +msgstr "Eksternt ID" #. openerp-web #: addons/web/static/src/js/formats.js:300 #: addons/web/static/src/js/view_page.js:245 +#: addons/web/static/src/js/formats.js:322 +#: addons/web/static/src/js/view_page.js:251 msgid "Download" -msgstr "" +msgstr "Download" #. openerp-web #: addons/web/static/src/js/formats.js:305 +#: addons/web/static/src/js/formats.js:327 #, python-format msgid "Download \"%s\"" -msgstr "" +msgstr "Download \"%s\"" #. openerp-web #: addons/web/static/src/js/search.js:191 @@ -202,61 +214,72 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:242 #: addons/web/static/src/js/search.js:291 +#: addons/web/static/src/js/search.js:296 msgid "OK" -msgstr "" +msgstr "OK" #. openerp-web #: addons/web/static/src/js/search.js:286 #: addons/web/static/src/xml/base.xml:1292 +#: addons/web/static/src/js/search.js:291 msgid "Add to Dashboard" -msgstr "" +msgstr "Tilføj til kontrolpanel" #. openerp-web #: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 msgid "Invalid Search" msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 msgid "triggered from search view" msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:503 +#: addons/web/static/src/js/search.js:508 #, python-format msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:839 +#: addons/web/static/src/js/search.js:844 msgid "not a valid integer" msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:853 +#: addons/web/static/src/js/search.js:858 msgid "not a valid number" msgstr "" #. openerp-web #: addons/web/static/src/js/search.js:931 #: addons/web/static/src/xml/base.xml:968 +#: addons/web/static/src/js/search.js:936 msgid "Yes" -msgstr "" +msgstr "Ja" #. openerp-web #: addons/web/static/src/js/search.js:932 +#: addons/web/static/src/js/search.js:937 msgid "No" -msgstr "" +msgstr "Nej" #. openerp-web #: addons/web/static/src/js/search.js:1290 +#: addons/web/static/src/js/search.js:1295 msgid "contains" -msgstr "" +msgstr "indeholder" #. openerp-web #: addons/web/static/src/js/search.js:1291 +#: addons/web/static/src/js/search.js:1296 msgid "doesn't contain" -msgstr "" +msgstr "indeholder ikke" #. openerp-web #: addons/web/static/src/js/search.js:1292 @@ -264,8 +287,13 @@ msgstr "" #: addons/web/static/src/js/search.js:1325 #: addons/web/static/src/js/search.js:1344 #: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 msgid "is equal to" -msgstr "" +msgstr "er lig med" #. openerp-web #: addons/web/static/src/js/search.js:1293 @@ -273,8 +301,13 @@ msgstr "" #: addons/web/static/src/js/search.js:1326 #: addons/web/static/src/js/search.js:1345 #: addons/web/static/src/js/search.js:1366 +#: addons/web/static/src/js/search.js:1298 +#: addons/web/static/src/js/search.js:1312 +#: addons/web/static/src/js/search.js:1331 +#: addons/web/static/src/js/search.js:1350 +#: addons/web/static/src/js/search.js:1371 msgid "is not equal to" -msgstr "" +msgstr "er ikke lig med" #. openerp-web #: addons/web/static/src/js/search.js:1294 @@ -282,8 +315,13 @@ msgstr "" #: addons/web/static/src/js/search.js:1327 #: addons/web/static/src/js/search.js:1346 #: addons/web/static/src/js/search.js:1367 +#: addons/web/static/src/js/search.js:1299 +#: addons/web/static/src/js/search.js:1313 +#: addons/web/static/src/js/search.js:1332 +#: addons/web/static/src/js/search.js:1351 +#: addons/web/static/src/js/search.js:1372 msgid "greater than" -msgstr "" +msgstr "større end" #. openerp-web #: addons/web/static/src/js/search.js:1295 @@ -291,8 +329,13 @@ msgstr "" #: addons/web/static/src/js/search.js:1328 #: addons/web/static/src/js/search.js:1347 #: addons/web/static/src/js/search.js:1368 +#: addons/web/static/src/js/search.js:1300 +#: addons/web/static/src/js/search.js:1314 +#: addons/web/static/src/js/search.js:1333 +#: addons/web/static/src/js/search.js:1352 +#: addons/web/static/src/js/search.js:1373 msgid "less than" -msgstr "" +msgstr "mindre end" #. openerp-web #: addons/web/static/src/js/search.js:1296 @@ -300,8 +343,13 @@ msgstr "" #: addons/web/static/src/js/search.js:1329 #: addons/web/static/src/js/search.js:1348 #: addons/web/static/src/js/search.js:1369 +#: addons/web/static/src/js/search.js:1301 +#: addons/web/static/src/js/search.js:1315 +#: addons/web/static/src/js/search.js:1334 +#: addons/web/static/src/js/search.js:1353 +#: addons/web/static/src/js/search.js:1374 msgid "greater or equal than" -msgstr "" +msgstr "større end eller lig med" #. openerp-web #: addons/web/static/src/js/search.js:1297 @@ -309,35 +357,45 @@ msgstr "" #: addons/web/static/src/js/search.js:1330 #: addons/web/static/src/js/search.js:1349 #: addons/web/static/src/js/search.js:1370 +#: addons/web/static/src/js/search.js:1302 +#: addons/web/static/src/js/search.js:1316 +#: addons/web/static/src/js/search.js:1335 +#: addons/web/static/src/js/search.js:1354 +#: addons/web/static/src/js/search.js:1375 msgid "less or equal than" -msgstr "" +msgstr "mindre eller lig med" #. openerp-web #: addons/web/static/src/js/search.js:1360 #: addons/web/static/src/js/search.js:1383 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1388 msgid "is" -msgstr "" +msgstr "er" #. openerp-web #: addons/web/static/src/js/search.js:1384 +#: addons/web/static/src/js/search.js:1389 msgid "is not" -msgstr "" +msgstr "er ikke" #. openerp-web #: addons/web/static/src/js/search.js:1396 +#: addons/web/static/src/js/search.js:1401 msgid "is true" -msgstr "" +msgstr "er sandt" #. openerp-web #: addons/web/static/src/js/search.js:1397 +#: addons/web/static/src/js/search.js:1402 msgid "is false" -msgstr "" +msgstr "er falsk" #. openerp-web #: addons/web/static/src/js/view_editor.js:20 #, python-format msgid "Manage Views (%s)" -msgstr "" +msgstr "Håndter Visninger (%s)" #. openerp-web #: addons/web/static/src/js/view_editor.js:46 @@ -353,7 +411,7 @@ msgstr "Opret" #: addons/web/static/src/xml/base.xml:483 #: addons/web/static/src/xml/base.xml:755 msgid "Edit" -msgstr "" +msgstr "Redigér" #. openerp-web #: addons/web/static/src/js/view_editor.js:48 @@ -365,18 +423,18 @@ msgstr "Fjern" #: addons/web/static/src/js/view_editor.js:71 #, python-format msgid "Create a view (%s)" -msgstr "" +msgstr "Opret en visning (%s)" #. openerp-web #: addons/web/static/src/js/view_editor.js:168 msgid "Do you really want to remove this view?" -msgstr "" +msgstr "Vil du virkelig slette denne visning" #. openerp-web #: addons/web/static/src/js/view_editor.js:364 #, python-format msgid "View Editor %d - %s" -msgstr "" +msgstr "Vis redigeringsværktøj %d - %s" #. openerp-web #: addons/web/static/src/js/view_editor.js:367 @@ -391,44 +449,46 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/view_editor.js:381 msgid "Preview" -msgstr "" +msgstr "Forhåndsvisning" #. openerp-web #: addons/web/static/src/js/view_editor.js:501 msgid "Do you really want to remove this node?" -msgstr "" +msgstr "Vil du virkelig fjerne dette led?" #. openerp-web #: addons/web/static/src/js/view_editor.js:815 #: addons/web/static/src/js/view_editor.js:939 msgid "Properties" -msgstr "" +msgstr "Egenskaber" #. openerp-web #: addons/web/static/src/js/view_editor.js:818 #: addons/web/static/src/js/view_editor.js:942 msgid "Update" -msgstr "" +msgstr "Opdatér" #. openerp-web #: addons/web/static/src/js/view_form.js:16 msgid "Form" -msgstr "" +msgstr "Formular" #. openerp-web #: addons/web/static/src/js/view_form.js:121 #: addons/web/static/src/js/views.js:803 msgid "Customize" -msgstr "" +msgstr "Tilpasse" #. openerp-web #: addons/web/static/src/js/view_form.js:123 #: addons/web/static/src/js/view_form.js:686 +#: addons/web/static/src/js/view_form.js:692 msgid "Set Default" -msgstr "" +msgstr "Indstil som standard" #. openerp-web #: addons/web/static/src/js/view_form.js:469 +#: addons/web/static/src/js/view_form.js:475 msgid "" "Warning, the record has been modified, your changes will be discarded." msgstr "" @@ -437,40 +497,47 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:693 +#: addons/web/static/src/js/view_form.js:699 msgid "Save default" -msgstr "" +msgstr "Gem som standard" #. openerp-web #: addons/web/static/src/js/view_form.js:754 +#: addons/web/static/src/js/view_form.js:760 msgid "Attachments" -msgstr "" +msgstr "vedhæftet fil" #. openerp-web #: addons/web/static/src/js/view_form.js:792 +#: addons/web/static/src/js/view_form.js:798 #, python-format msgid "Do you really want to delete the attachment %s?" -msgstr "" +msgstr "Vil du virkelig slette den vedhæftede fil %r?" #. openerp-web #: addons/web/static/src/js/view_form.js:822 +#: addons/web/static/src/js/view_form.js:828 #, python-format msgid "Unknown operator %s in domain %s" msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:830 +#: addons/web/static/src/js/view_form.js:836 #, python-format msgid "Unknown field %s in domain %s" msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:868 +#: addons/web/static/src/js/view_form.js:874 #, python-format msgid "Unsupported operator %s in domain %s" msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:1225 +#: addons/web/static/src/js/view_form.js:1231 msgid "Confirm" msgstr "" @@ -478,34 +545,43 @@ msgstr "" #: addons/web/static/src/js/view_form.js:1921 #: addons/web/static/src/js/view_form.js:2578 #: addons/web/static/src/js/view_form.js:2741 +#: addons/web/static/src/js/view_form.js:1933 +#: addons/web/static/src/js/view_form.js:2590 +#: addons/web/static/src/js/view_form.js:2760 msgid "Open: " msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:2049 +#: addons/web/static/src/js/view_form.js:2061 msgid "<em> Search More...</em>" msgstr "<em> Søg efter mere....</em>" #. openerp-web #: addons/web/static/src/js/view_form.js:2062 +#: addons/web/static/src/js/view_form.js:2074 #, python-format msgid "<em> Create \"<strong>%s</strong>\"</em>" msgstr "<em> Create \"<strong>%s</strong>\"</em>" #. openerp-web #: addons/web/static/src/js/view_form.js:2068 +#: addons/web/static/src/js/view_form.js:2080 msgid "<em> Create and Edit...</em>" msgstr "<em> Opret og rediger...</em>" #. openerp-web #: addons/web/static/src/js/view_form.js:2101 #: addons/web/static/src/js/views.js:675 +#: addons/web/static/src/js/view_form.js:2113 msgid "Search: " msgstr "" #. openerp-web #: addons/web/static/src/js/view_form.js:2101 #: addons/web/static/src/js/view_form.js:2550 +#: addons/web/static/src/js/view_form.js:2113 +#: addons/web/static/src/js/view_form.js:2562 msgid "Create: " msgstr "" @@ -514,11 +590,13 @@ msgstr "" #: addons/web/static/src/xml/base.xml:750 #: addons/web/static/src/xml/base.xml:772 #: addons/web/static/src/xml/base.xml:1646 +#: addons/web/static/src/js/view_form.js:2680 msgid "Add" msgstr "Tilføj" #. openerp-web #: addons/web/static/src/js/view_form.js:2721 +#: addons/web/static/src/js/view_form.js:2740 msgid "Add: " msgstr "" @@ -534,22 +612,26 @@ msgstr "" #. openerp-web #: addons/web/static/src/js/view_list.js:305 +#: addons/web/static/src/js/view_list.js:309 #, python-format msgid "[%(first_record)d to %(last_record)d] of %(records_count)d" msgstr "" #. openerp-web #: addons/web/static/src/js/view_list.js:524 +#: addons/web/static/src/js/view_list.js:528 msgid "Do you really want to remove these records?" msgstr "" #. openerp-web #: addons/web/static/src/js/view_list.js:1230 +#: addons/web/static/src/js/view_list.js:1232 msgid "Undefined" msgstr "" #. openerp-web #: addons/web/static/src/js/view_list.js:1327 +#: addons/web/static/src/js/view_list.js:1331 #, python-format msgid "%(page)d/%(page_count)d" msgstr "" @@ -570,7 +652,8 @@ msgid "Tree" msgstr "" #. openerp-web -#: addons/web/static/src/js/views.js:565 addons/web/static/src/xml/base.xml:480 +#: addons/web/static/src/js/views.js:565 +#: addons/web/static/src/xml/base.xml:480 msgid "Fields View Get" msgstr "" @@ -587,7 +670,8 @@ msgid "Model %s fields" msgstr "" #. openerp-web -#: addons/web/static/src/js/views.js:610 addons/web/static/src/xml/base.xml:482 +#: addons/web/static/src/js/views.js:610 +#: addons/web/static/src/xml/base.xml:482 msgid "Manage Views" msgstr "" @@ -654,12 +738,14 @@ msgid "Translations" msgstr "Oversættelser" #. openerp-web -#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 msgid "Powered by" msgstr "Drevet af" #. openerp-web -#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 #: addons/web/static/src/xml/base.xml:1813 msgid "OpenERP" msgstr "OpenERP" @@ -675,12 +761,14 @@ msgid "CREATE DATABASE" msgstr "OPRET DATABASE" #. openerp-web -#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211 +#: addons/web/static/src/xml/base.xml:68 +#: addons/web/static/src/xml/base.xml:211 msgid "Master password:" msgstr "Hovedadgangskode" #. openerp-web -#: addons/web/static/src/xml/base.xml:72 addons/web/static/src/xml/base.xml:191 +#: addons/web/static/src/xml/base.xml:72 +#: addons/web/static/src/xml/base.xml:191 msgid "New database name:" msgstr "Nyt database navn:" diff --git a/addons/web/i18n/es_CL.po b/addons/web/i18n/es_CL.po new file mode 100644 index 00000000000..261d8e8d9e4 --- /dev/null +++ b/addons/web/i18n/es_CL.po @@ -0,0 +1,1547 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-14 15:27+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:172 +#: addons/web/static/src/js/chrome.js:198 +#: addons/web/static/src/js/chrome.js:414 +#: addons/web/static/src/js/view_form.js:419 +#: addons/web/static/src/js/view_form.js:1233 +#: addons/web/static/src/xml/base.xml:1695 +#: addons/web/static/src/js/view_form.js:424 +#: addons/web/static/src/js/view_form.js:1239 +msgid "Ok" +msgstr "Ok" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:180 +msgid "Send OpenERP Enterprise Report" +msgstr "Enviar informe OpenERP Enterprise" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:194 +msgid "Dont send" +msgstr "No enviar" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:256 +#, python-format +msgid "Loading (%d)" +msgstr "Cargando (%d)" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:288 +msgid "Invalid database name" +msgstr "Nombre de base de datos incorrecto" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:483 +msgid "Backed" +msgstr "Guardado" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:484 +msgid "Database backed up successfully" +msgstr "Base de datos guardada correctamente" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Restored" +msgstr "Restaurada" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Database restored successfully" +msgstr "Base de datos restaurada correctamente" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:708 +#: addons/web/static/src/xml/base.xml:359 +msgid "About" +msgstr "Acerca de" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:787 +#: addons/web/static/src/xml/base.xml:356 +msgid "Preferences" +msgstr "Preferencias" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:790 +#: addons/web/static/src/js/search.js:239 +#: addons/web/static/src/js/search.js:288 +#: addons/web/static/src/js/view_editor.js:95 +#: addons/web/static/src/js/view_editor.js:836 +#: addons/web/static/src/js/view_editor.js:962 +#: addons/web/static/src/js/view_form.js:1228 +#: addons/web/static/src/xml/base.xml:738 +#: addons/web/static/src/xml/base.xml:1496 +#: addons/web/static/src/xml/base.xml:1506 +#: addons/web/static/src/xml/base.xml:1515 +#: addons/web/static/src/js/search.js:293 +#: addons/web/static/src/js/view_form.js:1234 +msgid "Cancel" +msgstr "Cancelar" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:791 +msgid "Change password" +msgstr "Cambiar la Contraseña" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:792 +#: addons/web/static/src/js/view_editor.js:73 +#: addons/web/static/src/js/views.js:962 +#: addons/web/static/src/xml/base.xml:737 +#: addons/web/static/src/xml/base.xml:1500 +#: addons/web/static/src/xml/base.xml:1514 +msgid "Save" +msgstr "Guardar" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:811 +#: addons/web/static/src/xml/base.xml:226 +#: addons/web/static/src/xml/base.xml:1729 +msgid "Change Password" +msgstr "Cambiar Contraseña" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1096 +#: addons/web/static/src/js/chrome.js:1100 +msgid "OpenERP - Unsupported/Community Version" +msgstr "OpenERP - Versión Community" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1131 +#: addons/web/static/src/js/chrome.js:1135 +msgid "Client Error" +msgstr "Error en el cliente" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:6 +msgid "Export Data" +msgstr "Exportar datos" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:19 +#: addons/web/static/src/js/data_import.js:69 +#: addons/web/static/src/js/view_editor.js:49 +#: addons/web/static/src/js/view_editor.js:398 +#: addons/web/static/src/js/view_form.js:692 +#: addons/web/static/src/js/view_form.js:3044 +#: addons/web/static/src/js/views.js:963 +#: addons/web/static/src/js/view_form.js:698 +#: addons/web/static/src/js/view_form.js:3067 +msgid "Close" +msgstr "Cerrar" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:20 +msgid "Export To File" +msgstr "Exportar a fichero" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:125 +msgid "Please enter save field list name" +msgstr "Por favor, introduzca el nombre de la lista de campos a guardar" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:360 +msgid "Please select fields to save export list..." +msgstr "" +"Por favor, seleccione los campos para guardar la lista de exportación..." + +#. openerp-web +#: addons/web/static/src/js/data_export.js:373 +msgid "Please select fields to export..." +msgstr "Por favor, seleccione los campos a exportar" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:34 +msgid "Import Data" +msgstr "Importar datos" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:70 +msgid "Import File" +msgstr "Importar archivo" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:105 +msgid "External ID" +msgstr "ID externo" + +#. openerp-web +#: addons/web/static/src/js/formats.js:300 +#: addons/web/static/src/js/view_page.js:245 +#: addons/web/static/src/js/formats.js:322 +#: addons/web/static/src/js/view_page.js:251 +msgid "Download" +msgstr "Descargar" + +#. openerp-web +#: addons/web/static/src/js/formats.js:305 +#: addons/web/static/src/js/formats.js:327 +#, python-format +msgid "Download \"%s\"" +msgstr "Descargar \"%s\"" + +#. openerp-web +#: addons/web/static/src/js/search.js:191 +msgid "Filter disabled due to invalid syntax" +msgstr "Filtro desactivado debido a sintaxis inválida" + +#. openerp-web +#: addons/web/static/src/js/search.js:237 +msgid "Filter Entry" +msgstr "Entrada del filtro" + +#. openerp-web +#: addons/web/static/src/js/search.js:242 +#: addons/web/static/src/js/search.js:291 +#: addons/web/static/src/js/search.js:296 +msgid "OK" +msgstr "Aceptar" + +#. openerp-web +#: addons/web/static/src/js/search.js:286 +#: addons/web/static/src/xml/base.xml:1292 +#: addons/web/static/src/js/search.js:291 +msgid "Add to Dashboard" +msgstr "Añadir al tablero" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "Invalid Search" +msgstr "Búsqueda incorrecta" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "triggered from search view" +msgstr "Disparador activado desde la vista de búsqueda" + +#. openerp-web +#: addons/web/static/src/js/search.js:503 +#: addons/web/static/src/js/search.js:508 +#, python-format +msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" +msgstr "" +"Valor incorrecto para el campo %(fieldname)s: [%(value)s] es %(message)s" + +#. openerp-web +#: addons/web/static/src/js/search.js:839 +#: addons/web/static/src/js/search.js:844 +msgid "not a valid integer" +msgstr "entero no válido" + +#. openerp-web +#: addons/web/static/src/js/search.js:853 +#: addons/web/static/src/js/search.js:858 +msgid "not a valid number" +msgstr "no es un número válido" + +#. openerp-web +#: addons/web/static/src/js/search.js:931 +#: addons/web/static/src/xml/base.xml:968 +#: addons/web/static/src/js/search.js:936 +msgid "Yes" +msgstr "Sí" + +#. openerp-web +#: addons/web/static/src/js/search.js:932 +#: addons/web/static/src/js/search.js:937 +msgid "No" +msgstr "No" + +#. openerp-web +#: addons/web/static/src/js/search.js:1290 +#: addons/web/static/src/js/search.js:1295 +msgid "contains" +msgstr "contiene" + +#. openerp-web +#: addons/web/static/src/js/search.js:1291 +#: addons/web/static/src/js/search.js:1296 +msgid "doesn't contain" +msgstr "no contiene" + +#. openerp-web +#: addons/web/static/src/js/search.js:1292 +#: addons/web/static/src/js/search.js:1306 +#: addons/web/static/src/js/search.js:1325 +#: addons/web/static/src/js/search.js:1344 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +msgid "is equal to" +msgstr "es igual a" + +#. openerp-web +#: addons/web/static/src/js/search.js:1293 +#: addons/web/static/src/js/search.js:1307 +#: addons/web/static/src/js/search.js:1326 +#: addons/web/static/src/js/search.js:1345 +#: addons/web/static/src/js/search.js:1366 +#: addons/web/static/src/js/search.js:1298 +#: addons/web/static/src/js/search.js:1312 +#: addons/web/static/src/js/search.js:1331 +#: addons/web/static/src/js/search.js:1350 +#: addons/web/static/src/js/search.js:1371 +msgid "is not equal to" +msgstr "es distinto de" + +#. openerp-web +#: addons/web/static/src/js/search.js:1294 +#: addons/web/static/src/js/search.js:1308 +#: addons/web/static/src/js/search.js:1327 +#: addons/web/static/src/js/search.js:1346 +#: addons/web/static/src/js/search.js:1367 +#: addons/web/static/src/js/search.js:1299 +#: addons/web/static/src/js/search.js:1313 +#: addons/web/static/src/js/search.js:1332 +#: addons/web/static/src/js/search.js:1351 +#: addons/web/static/src/js/search.js:1372 +msgid "greater than" +msgstr "mayor que" + +#. openerp-web +#: addons/web/static/src/js/search.js:1295 +#: addons/web/static/src/js/search.js:1309 +#: addons/web/static/src/js/search.js:1328 +#: addons/web/static/src/js/search.js:1347 +#: addons/web/static/src/js/search.js:1368 +#: addons/web/static/src/js/search.js:1300 +#: addons/web/static/src/js/search.js:1314 +#: addons/web/static/src/js/search.js:1333 +#: addons/web/static/src/js/search.js:1352 +#: addons/web/static/src/js/search.js:1373 +msgid "less than" +msgstr "menor que" + +#. openerp-web +#: addons/web/static/src/js/search.js:1296 +#: addons/web/static/src/js/search.js:1310 +#: addons/web/static/src/js/search.js:1329 +#: addons/web/static/src/js/search.js:1348 +#: addons/web/static/src/js/search.js:1369 +#: addons/web/static/src/js/search.js:1301 +#: addons/web/static/src/js/search.js:1315 +#: addons/web/static/src/js/search.js:1334 +#: addons/web/static/src/js/search.js:1353 +#: addons/web/static/src/js/search.js:1374 +msgid "greater or equal than" +msgstr "mayor o igual que" + +#. openerp-web +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +#: addons/web/static/src/js/search.js:1302 +#: addons/web/static/src/js/search.js:1316 +#: addons/web/static/src/js/search.js:1335 +#: addons/web/static/src/js/search.js:1354 +#: addons/web/static/src/js/search.js:1375 +msgid "less or equal than" +msgstr "menor o igual que" + +#. openerp-web +#: addons/web/static/src/js/search.js:1360 +#: addons/web/static/src/js/search.js:1383 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1388 +msgid "is" +msgstr "es" + +#. openerp-web +#: addons/web/static/src/js/search.js:1384 +#: addons/web/static/src/js/search.js:1389 +msgid "is not" +msgstr "no es" + +#. openerp-web +#: addons/web/static/src/js/search.js:1396 +#: addons/web/static/src/js/search.js:1401 +msgid "is true" +msgstr "es verdadero" + +#. openerp-web +#: addons/web/static/src/js/search.js:1397 +#: addons/web/static/src/js/search.js:1402 +msgid "is false" +msgstr "es falso" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:20 +#, python-format +msgid "Manage Views (%s)" +msgstr "Gestionar vistas (%s)" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:46 +#: addons/web/static/src/js/view_list.js:17 +#: addons/web/static/src/xml/base.xml:100 +#: addons/web/static/src/xml/base.xml:327 +#: addons/web/static/src/xml/base.xml:756 +msgid "Create" +msgstr "Crear" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:47 +#: addons/web/static/src/xml/base.xml:483 +#: addons/web/static/src/xml/base.xml:755 +msgid "Edit" +msgstr "Editar" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:48 +#: addons/web/static/src/xml/base.xml:1647 +msgid "Remove" +msgstr "Eliminar" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:71 +#, python-format +msgid "Create a view (%s)" +msgstr "Crear una vista (%s)" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:168 +msgid "Do you really want to remove this view?" +msgstr "¿Realmente desea elimar esta vista?" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:364 +#, python-format +msgid "View Editor %d - %s" +msgstr "Ver Editor %d - %s" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:367 +msgid "Inherited View" +msgstr "Vista heredada" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:371 +msgid "Do you really wants to create an inherited view here?" +msgstr "¿Realmente desea crear una vista heredada aquí?" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:381 +msgid "Preview" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:501 +msgid "Do you really want to remove this node?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:815 +#: addons/web/static/src/js/view_editor.js:939 +msgid "Properties" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:818 +#: addons/web/static/src/js/view_editor.js:942 +msgid "Update" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:16 +msgid "Form" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:121 +#: addons/web/static/src/js/views.js:803 +msgid "Customize" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:123 +#: addons/web/static/src/js/view_form.js:686 +#: addons/web/static/src/js/view_form.js:692 +msgid "Set Default" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:469 +#: addons/web/static/src/js/view_form.js:475 +msgid "" +"Warning, the record has been modified, your changes will be discarded." +msgstr "" +"Advertencia, el registro se ha modificado, los cambios serán descartados." + +#. openerp-web +#: addons/web/static/src/js/view_form.js:693 +#: addons/web/static/src/js/view_form.js:699 +msgid "Save default" +msgstr "Guardar por defecto" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:754 +#: addons/web/static/src/js/view_form.js:760 +msgid "Attachments" +msgstr "Archivos adjuntos" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:792 +#: addons/web/static/src/js/view_form.js:798 +#, python-format +msgid "Do you really want to delete the attachment %s?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:822 +#: addons/web/static/src/js/view_form.js:828 +#, python-format +msgid "Unknown operator %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:830 +#: addons/web/static/src/js/view_form.js:836 +#, python-format +msgid "Unknown field %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:868 +#: addons/web/static/src/js/view_form.js:874 +#, python-format +msgid "Unsupported operator %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1225 +#: addons/web/static/src/js/view_form.js:1231 +msgid "Confirm" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1921 +#: addons/web/static/src/js/view_form.js:2578 +#: addons/web/static/src/js/view_form.js:2741 +#: addons/web/static/src/js/view_form.js:1933 +#: addons/web/static/src/js/view_form.js:2590 +#: addons/web/static/src/js/view_form.js:2760 +msgid "Open: " +msgstr "Abrir " + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2049 +#: addons/web/static/src/js/view_form.js:2061 +msgid "<em> Search More...</em>" +msgstr "<em> Buscar más...</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2062 +#: addons/web/static/src/js/view_form.js:2074 +#, python-format +msgid "<em> Create \"<strong>%s</strong>\"</em>" +msgstr "<em> Crear \"<strong>%s</strong>\"</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2068 +#: addons/web/static/src/js/view_form.js:2080 +msgid "<em> Create and Edit...</em>" +msgstr "<em> Crear y Editar...</em>" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/views.js:675 +#: addons/web/static/src/js/view_form.js:2113 +msgid "Search: " +msgstr "Buscar: " + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/view_form.js:2550 +#: addons/web/static/src/js/view_form.js:2113 +#: addons/web/static/src/js/view_form.js:2562 +msgid "Create: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2661 +#: addons/web/static/src/xml/base.xml:750 +#: addons/web/static/src/xml/base.xml:772 +#: addons/web/static/src/xml/base.xml:1646 +#: addons/web/static/src/js/view_form.js:2680 +msgid "Add" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2721 +#: addons/web/static/src/js/view_form.js:2740 +msgid "Add: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:8 +msgid "List" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:269 +msgid "Unlimited" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:305 +#: addons/web/static/src/js/view_list.js:309 +#, python-format +msgid "[%(first_record)d to %(last_record)d] of %(records_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:524 +#: addons/web/static/src/js/view_list.js:528 +msgid "Do you really want to remove these records?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1230 +#: addons/web/static/src/js/view_list.js:1232 +msgid "Undefined" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1327 +#: addons/web/static/src/js/view_list.js:1331 +#, python-format +msgid "%(page)d/%(page_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:8 +msgid "Page" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:52 +msgid "Do you really want to delete this record?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_tree.js:11 +msgid "Tree" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:565 +#: addons/web/static/src/xml/base.xml:480 +msgid "Fields View Get" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:573 +#, python-format +msgid "View Log (%s)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:600 +#, python-format +msgid "Model %s fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:610 +#: addons/web/static/src/xml/base.xml:482 +msgid "Manage Views" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:611 +msgid "Could not find current view declaration" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:805 +msgid "Translate" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:807 +msgid "Technical translation" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:811 +msgid "Other Options" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:814 +#: addons/web/static/src/xml/base.xml:1736 +msgid "Import" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:817 +#: addons/web/static/src/xml/base.xml:1606 +msgid "Export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Reports" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Actions" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Links" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:919 +msgid "You must choose at least one record." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:920 +msgid "Warning" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:957 +msgid "Translations" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +msgid "Powered by" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:1813 +msgid "OpenERP" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:52 +msgid "Loading..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:61 +msgid "CREATE DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:68 +#: addons/web/static/src/xml/base.xml:211 +msgid "Master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:72 +#: addons/web/static/src/xml/base.xml:191 +msgid "New database name:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:77 +msgid "Load Demonstration data:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:81 +msgid "Default language:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:91 +msgid "Admin password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:95 +msgid "Confirm password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:109 +msgid "DROP DATABASE" +msgstr "" + +#. 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 "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:128 +#: addons/web/static/src/xml/base.xml:162 +#: addons/web/static/src/xml/base.xml:187 +msgid "Master Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:132 +#: addons/web/static/src/xml/base.xml:328 +msgid "Drop" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:143 +msgid "BACKUP DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:166 +#: addons/web/static/src/xml/base.xml:329 +msgid "Backup" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:175 +msgid "RESTORE DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:182 +msgid "File:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:195 +#: addons/web/static/src/xml/base.xml:330 +msgid "Restore" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:204 +msgid "CHANGE MASTER PASSWORD" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:216 +msgid "New master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:221 +msgid "Confirm new master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "" +"Your version of OpenERP is unsupported. Support & maintenance services are " +"available here:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "OpenERP Entreprise" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:256 +msgid "OpenERP Enterprise Contract." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:257 +msgid "Your report will be sent to the OpenERP Enterprise team." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:259 +msgid "Summary:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:263 +msgid "Description:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:267 +msgid "What you did:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:297 +msgid "Invalid username or password" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:306 +msgid "Username" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:308 +#: addons/web/static/src/xml/base.xml:331 +msgid "Password" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:310 +msgid "Log in" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:314 +msgid "Manage Databases" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:332 +msgid "Back to Login" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:353 +msgid "Home" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:363 +msgid "LOGOUT" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:388 +msgid "Fold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:389 +msgid "Unfold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:454 +msgid "Hide this tip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:455 +msgid "Disable all tips" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:463 +msgid "Add / Remove Shortcut..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:471 +msgid "More…" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:477 +msgid "Debug View#" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:478 +msgid "View Log (perm_read)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:479 +msgid "View Fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:483 +msgid "View" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:484 +msgid "Edit SearchView" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:485 +msgid "Edit Action" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:486 +msgid "Edit Workflow" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:491 +msgid "ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:494 +msgid "XML ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:497 +msgid "Creation User:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:500 +msgid "Creation Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:503 +msgid "Latest Modification by:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:506 +msgid "Latest Modification Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:542 +msgid "Field" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:632 +#: addons/web/static/src/xml/base.xml:758 +#: addons/web/static/src/xml/base.xml:1708 +msgid "Delete" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:757 +msgid "Duplicate" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:775 +msgid "Add attachment" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:801 +msgid "Default:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:818 +msgid "Condition:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:837 +msgid "Only you" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:844 +msgid "All users" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:851 +msgid "Unhandled widget" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:900 +msgid "Notebook Page \"" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:905 +#: addons/web/static/src/xml/base.xml:964 +msgid "Modifiers:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:931 +msgid "(nolabel)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:936 +msgid "Field:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:940 +msgid "Object:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:944 +msgid "Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:948 +msgid "Widget:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:952 +msgid "Size:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:956 +msgid "Context:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:960 +msgid "Domain:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:968 +msgid "Change default:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:972 +msgid "On change:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:976 +msgid "Relation:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:980 +msgid "Selection:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1020 +msgid "Send an e-mail with your default e-mail client" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1034 +msgid "Open this resource" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1056 +msgid "Select date" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1090 +msgid "Open..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1091 +msgid "Create..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1092 +msgid "Search..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1095 +msgid "..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1155 +#: addons/web/static/src/xml/base.xml:1198 +msgid "Set Image" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1163 +#: addons/web/static/src/xml/base.xml:1213 +#: addons/web/static/src/xml/base.xml:1215 +#: addons/web/static/src/xml/base.xml:1272 +msgid "Clear" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1172 +#: addons/web/static/src/xml/base.xml:1223 +msgid "Uploading ..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1200 +#: addons/web/static/src/xml/base.xml:1495 +msgid "Select" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1207 +#: addons/web/static/src/xml/base.xml:1209 +msgid "Save As" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1238 +msgid "Button" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1241 +msgid "(no string)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1248 +msgid "Special:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1253 +msgid "Button Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1257 +msgid "Method:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1261 +msgid "Action ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1271 +msgid "Search" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1279 +msgid "Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1280 +msgid "-- Filters --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1289 +msgid "-- Actions --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1290 +msgid "Add Advanced Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1291 +msgid "Save Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1293 +msgid "Manage Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1298 +msgid "Filter Name:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1300 +msgid "(Any existing filter with the same name will be replaced)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1305 +msgid "Select Dashboard to add this filter to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1309 +msgid "Title of new Dashboard item:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1416 +msgid "Advanced Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1426 +msgid "Any of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1427 +msgid "All the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1428 +msgid "None of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1435 +msgid "Add condition" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1436 +msgid "and" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1503 +msgid "Save & New" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1504 +msgid "Save & Close" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1611 +msgid "" +"This wizard will export all data that matches the current search criteria to " +"a CSV file.\n" +" You can export all data or only the fields that can be " +"reimported after modification." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1618 +msgid "Export Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1620 +msgid "Import Compatible Export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1621 +msgid "Export all Data" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1624 +msgid "Export Formats" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1630 +msgid "Available fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1632 +msgid "Fields to export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1634 +msgid "Save fields list" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1648 +msgid "Remove All" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1660 +msgid "Name" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1693 +msgid "Save as:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1700 +msgid "Saved exports:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1714 +msgid "Old Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1719 +msgid "New Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1724 +msgid "Confirm Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1742 +msgid "1. Import a .CSV file" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1743 +msgid "" +"Select a .CSV file to import. If you need a sample of file to import,\n" +" you should use the export tool with the \"Import Compatible\" option." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1747 +msgid "CSV File:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1750 +msgid "2. Check your file format" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1753 +msgid "Import Options" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1757 +msgid "Does your file have titles?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1763 +msgid "Separator:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1765 +msgid "Delimiter:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1769 +msgid "Encoding:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1772 +msgid "UTF-8" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1773 +msgid "Latin 1" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "Lines to skip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "" +"For use if CSV files have titles on multiple lines, skips more than a single " +"line during import" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1803 +msgid "The import failed due to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1805 +msgid "Here is a preview of the file we could not import:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1812 +msgid "Activate the developper mode" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1814 +msgid "Version" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1815 +msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1816 +msgid "OpenERP is a trademark of the" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1817 +msgid "OpenERP SA Company" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1819 +msgid "Licenced under the terms of" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1820 +msgid "GNU Affero General Public License" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1822 +msgid "For more information visit" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1823 +msgid "OpenERP.com" +msgstr "" diff --git a/addons/web/i18n/gu.po b/addons/web/i18n/gu.po new file mode 100644 index 00000000000..4f675076ece --- /dev/null +++ b/addons/web/i18n/gu.po @@ -0,0 +1,1544 @@ +# Gujarati translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-16 11:31+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Gujarati <gu@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-04-17 05:24+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:172 +#: addons/web/static/src/js/chrome.js:198 +#: addons/web/static/src/js/chrome.js:414 +#: addons/web/static/src/js/view_form.js:419 +#: addons/web/static/src/js/view_form.js:1233 +#: addons/web/static/src/xml/base.xml:1695 +#: addons/web/static/src/js/view_form.js:424 +#: addons/web/static/src/js/view_form.js:1239 +msgid "Ok" +msgstr "બરાબર" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:180 +msgid "Send OpenERP Enterprise Report" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:194 +msgid "Dont send" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:256 +#, python-format +msgid "Loading (%d)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:288 +msgid "Invalid database name" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:483 +msgid "Backed" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:484 +msgid "Database backed up successfully" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Restored" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:527 +msgid "Database restored successfully" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:708 +#: addons/web/static/src/xml/base.xml:359 +msgid "About" +msgstr "ના વિશે" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:787 +#: addons/web/static/src/xml/base.xml:356 +msgid "Preferences" +msgstr "સામાન્ય પસંદગીઓ" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:790 +#: addons/web/static/src/js/search.js:239 +#: addons/web/static/src/js/search.js:288 +#: addons/web/static/src/js/view_editor.js:95 +#: addons/web/static/src/js/view_editor.js:836 +#: addons/web/static/src/js/view_editor.js:962 +#: addons/web/static/src/js/view_form.js:1228 +#: addons/web/static/src/xml/base.xml:738 +#: addons/web/static/src/xml/base.xml:1496 +#: addons/web/static/src/xml/base.xml:1506 +#: addons/web/static/src/xml/base.xml:1515 +#: addons/web/static/src/js/search.js:293 +#: addons/web/static/src/js/view_form.js:1234 +msgid "Cancel" +msgstr "રદ કરો" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:791 +msgid "Change password" +msgstr "પાસવર્ડ બદલો" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:792 +#: addons/web/static/src/js/view_editor.js:73 +#: addons/web/static/src/js/views.js:962 +#: addons/web/static/src/xml/base.xml:737 +#: addons/web/static/src/xml/base.xml:1500 +#: addons/web/static/src/xml/base.xml:1514 +msgid "Save" +msgstr "સાચવો" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:811 +#: addons/web/static/src/xml/base.xml:226 +#: addons/web/static/src/xml/base.xml:1729 +msgid "Change Password" +msgstr "પાસવર્ડ બદલો" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1096 +#: addons/web/static/src/js/chrome.js:1100 +msgid "OpenERP - Unsupported/Community Version" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/chrome.js:1131 +#: addons/web/static/src/js/chrome.js:1135 +msgid "Client Error" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:6 +msgid "Export Data" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:19 +#: addons/web/static/src/js/data_import.js:69 +#: addons/web/static/src/js/view_editor.js:49 +#: addons/web/static/src/js/view_editor.js:398 +#: addons/web/static/src/js/view_form.js:692 +#: addons/web/static/src/js/view_form.js:3044 +#: addons/web/static/src/js/views.js:963 +#: addons/web/static/src/js/view_form.js:698 +#: addons/web/static/src/js/view_form.js:3067 +msgid "Close" +msgstr "બંધ કરો" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:20 +msgid "Export To File" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:125 +msgid "Please enter save field list name" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:360 +msgid "Please select fields to save export list..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_export.js:373 +msgid "Please select fields to export..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:34 +msgid "Import Data" +msgstr "માહિતી આયાત કરો" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:70 +msgid "Import File" +msgstr "ફાઈલ આયાત કરો" + +#. openerp-web +#: addons/web/static/src/js/data_import.js:105 +msgid "External ID" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/formats.js:300 +#: addons/web/static/src/js/view_page.js:245 +#: addons/web/static/src/js/formats.js:322 +#: addons/web/static/src/js/view_page.js:251 +msgid "Download" +msgstr "ડાઉનલોડ" + +#. openerp-web +#: addons/web/static/src/js/formats.js:305 +#: addons/web/static/src/js/formats.js:327 +#, python-format +msgid "Download \"%s\"" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:191 +msgid "Filter disabled due to invalid syntax" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:237 +msgid "Filter Entry" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:242 +#: addons/web/static/src/js/search.js:291 +#: addons/web/static/src/js/search.js:296 +msgid "OK" +msgstr "ઓકે" + +#. openerp-web +#: addons/web/static/src/js/search.js:286 +#: addons/web/static/src/xml/base.xml:1292 +#: addons/web/static/src/js/search.js:291 +msgid "Add to Dashboard" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "Invalid Search" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:415 +#: addons/web/static/src/js/search.js:420 +msgid "triggered from search view" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:503 +#: addons/web/static/src/js/search.js:508 +#, python-format +msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:839 +#: addons/web/static/src/js/search.js:844 +msgid "not a valid integer" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:853 +#: addons/web/static/src/js/search.js:858 +msgid "not a valid number" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:931 +#: addons/web/static/src/xml/base.xml:968 +#: addons/web/static/src/js/search.js:936 +msgid "Yes" +msgstr "હાં" + +#. openerp-web +#: addons/web/static/src/js/search.js:932 +#: addons/web/static/src/js/search.js:937 +msgid "No" +msgstr "નહીં" + +#. openerp-web +#: addons/web/static/src/js/search.js:1290 +#: addons/web/static/src/js/search.js:1295 +msgid "contains" +msgstr "સમાવે છે" + +#. openerp-web +#: addons/web/static/src/js/search.js:1291 +#: addons/web/static/src/js/search.js:1296 +msgid "doesn't contain" +msgstr "સમાવતું નથી" + +#. openerp-web +#: addons/web/static/src/js/search.js:1292 +#: addons/web/static/src/js/search.js:1306 +#: addons/web/static/src/js/search.js:1325 +#: addons/web/static/src/js/search.js:1344 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +msgid "is equal to" +msgstr "ને બરાબર છે" + +#. openerp-web +#: addons/web/static/src/js/search.js:1293 +#: addons/web/static/src/js/search.js:1307 +#: addons/web/static/src/js/search.js:1326 +#: addons/web/static/src/js/search.js:1345 +#: addons/web/static/src/js/search.js:1366 +#: addons/web/static/src/js/search.js:1298 +#: addons/web/static/src/js/search.js:1312 +#: addons/web/static/src/js/search.js:1331 +#: addons/web/static/src/js/search.js:1350 +#: addons/web/static/src/js/search.js:1371 +msgid "is not equal to" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1294 +#: addons/web/static/src/js/search.js:1308 +#: addons/web/static/src/js/search.js:1327 +#: addons/web/static/src/js/search.js:1346 +#: addons/web/static/src/js/search.js:1367 +#: addons/web/static/src/js/search.js:1299 +#: addons/web/static/src/js/search.js:1313 +#: addons/web/static/src/js/search.js:1332 +#: addons/web/static/src/js/search.js:1351 +#: addons/web/static/src/js/search.js:1372 +msgid "greater than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1295 +#: addons/web/static/src/js/search.js:1309 +#: addons/web/static/src/js/search.js:1328 +#: addons/web/static/src/js/search.js:1347 +#: addons/web/static/src/js/search.js:1368 +#: addons/web/static/src/js/search.js:1300 +#: addons/web/static/src/js/search.js:1314 +#: addons/web/static/src/js/search.js:1333 +#: addons/web/static/src/js/search.js:1352 +#: addons/web/static/src/js/search.js:1373 +msgid "less than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1296 +#: addons/web/static/src/js/search.js:1310 +#: addons/web/static/src/js/search.js:1329 +#: addons/web/static/src/js/search.js:1348 +#: addons/web/static/src/js/search.js:1369 +#: addons/web/static/src/js/search.js:1301 +#: addons/web/static/src/js/search.js:1315 +#: addons/web/static/src/js/search.js:1334 +#: addons/web/static/src/js/search.js:1353 +#: addons/web/static/src/js/search.js:1374 +msgid "greater or equal than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1297 +#: addons/web/static/src/js/search.js:1311 +#: addons/web/static/src/js/search.js:1330 +#: addons/web/static/src/js/search.js:1349 +#: addons/web/static/src/js/search.js:1370 +#: addons/web/static/src/js/search.js:1302 +#: addons/web/static/src/js/search.js:1316 +#: addons/web/static/src/js/search.js:1335 +#: addons/web/static/src/js/search.js:1354 +#: addons/web/static/src/js/search.js:1375 +msgid "less or equal than" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1360 +#: addons/web/static/src/js/search.js:1383 +#: addons/web/static/src/js/search.js:1365 +#: addons/web/static/src/js/search.js:1388 +msgid "is" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1384 +#: addons/web/static/src/js/search.js:1389 +msgid "is not" +msgstr "એ નથી" + +#. openerp-web +#: addons/web/static/src/js/search.js:1396 +#: addons/web/static/src/js/search.js:1401 +msgid "is true" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/search.js:1397 +#: addons/web/static/src/js/search.js:1402 +msgid "is false" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:20 +#, python-format +msgid "Manage Views (%s)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:46 +#: addons/web/static/src/js/view_list.js:17 +#: addons/web/static/src/xml/base.xml:100 +#: addons/web/static/src/xml/base.xml:327 +#: addons/web/static/src/xml/base.xml:756 +msgid "Create" +msgstr "બનાવો" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:47 +#: addons/web/static/src/xml/base.xml:483 +#: addons/web/static/src/xml/base.xml:755 +msgid "Edit" +msgstr "ફેરફાર કરો" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:48 +#: addons/web/static/src/xml/base.xml:1647 +msgid "Remove" +msgstr "દૂર કરો" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:71 +#, python-format +msgid "Create a view (%s)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:168 +msgid "Do you really want to remove this view?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:364 +#, python-format +msgid "View Editor %d - %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:367 +msgid "Inherited View" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:371 +msgid "Do you really wants to create an inherited view here?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:381 +msgid "Preview" +msgstr "પૂર્વદર્શન" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:501 +msgid "Do you really want to remove this node?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:815 +#: addons/web/static/src/js/view_editor.js:939 +msgid "Properties" +msgstr "ગુણધર્મો" + +#. openerp-web +#: addons/web/static/src/js/view_editor.js:818 +#: addons/web/static/src/js/view_editor.js:942 +msgid "Update" +msgstr "સુધારો" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:16 +msgid "Form" +msgstr "ફોર્મ" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:121 +#: addons/web/static/src/js/views.js:803 +msgid "Customize" +msgstr "વૈવિધ્યપૂર્ણ" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:123 +#: addons/web/static/src/js/view_form.js:686 +#: addons/web/static/src/js/view_form.js:692 +msgid "Set Default" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:469 +#: addons/web/static/src/js/view_form.js:475 +msgid "" +"Warning, the record has been modified, your changes will be discarded." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:693 +#: addons/web/static/src/js/view_form.js:699 +msgid "Save default" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:754 +#: addons/web/static/src/js/view_form.js:760 +msgid "Attachments" +msgstr "જોડાણો" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:792 +#: addons/web/static/src/js/view_form.js:798 +#, python-format +msgid "Do you really want to delete the attachment %s?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:822 +#: addons/web/static/src/js/view_form.js:828 +#, python-format +msgid "Unknown operator %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:830 +#: addons/web/static/src/js/view_form.js:836 +#, python-format +msgid "Unknown field %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:868 +#: addons/web/static/src/js/view_form.js:874 +#, python-format +msgid "Unsupported operator %s in domain %s" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1225 +#: addons/web/static/src/js/view_form.js:1231 +msgid "Confirm" +msgstr "ખાતરી" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:1921 +#: addons/web/static/src/js/view_form.js:2578 +#: addons/web/static/src/js/view_form.js:2741 +#: addons/web/static/src/js/view_form.js:1933 +#: addons/web/static/src/js/view_form.js:2590 +#: addons/web/static/src/js/view_form.js:2760 +msgid "Open: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2049 +#: addons/web/static/src/js/view_form.js:2061 +msgid "<em> Search More...</em>" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2062 +#: addons/web/static/src/js/view_form.js:2074 +#, python-format +msgid "<em> Create \"<strong>%s</strong>\"</em>" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2068 +#: addons/web/static/src/js/view_form.js:2080 +msgid "<em> Create and Edit...</em>" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/views.js:675 +#: addons/web/static/src/js/view_form.js:2113 +msgid "Search: " +msgstr "શોધો: " + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2101 +#: addons/web/static/src/js/view_form.js:2550 +#: addons/web/static/src/js/view_form.js:2113 +#: addons/web/static/src/js/view_form.js:2562 +msgid "Create: " +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2661 +#: addons/web/static/src/xml/base.xml:750 +#: addons/web/static/src/xml/base.xml:772 +#: addons/web/static/src/xml/base.xml:1646 +#: addons/web/static/src/js/view_form.js:2680 +msgid "Add" +msgstr "ઉમેરવું" + +#. openerp-web +#: addons/web/static/src/js/view_form.js:2721 +#: addons/web/static/src/js/view_form.js:2740 +msgid "Add: " +msgstr "ઉમેરવું: " + +#. openerp-web +#: addons/web/static/src/js/view_list.js:8 +msgid "List" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:269 +msgid "Unlimited" +msgstr "અમર્યાદિત" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:305 +#: addons/web/static/src/js/view_list.js:309 +#, python-format +msgid "[%(first_record)d to %(last_record)d] of %(records_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:524 +#: addons/web/static/src/js/view_list.js:528 +msgid "Do you really want to remove these records?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1230 +#: addons/web/static/src/js/view_list.js:1232 +msgid "Undefined" +msgstr "અવ્યાખ્યાયિત" + +#. openerp-web +#: addons/web/static/src/js/view_list.js:1327 +#: addons/web/static/src/js/view_list.js:1331 +#, python-format +msgid "%(page)d/%(page_count)d" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:8 +msgid "Page" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_page.js:52 +msgid "Do you really want to delete this record?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/view_tree.js:11 +msgid "Tree" +msgstr "ટ્રી" + +#. openerp-web +#: addons/web/static/src/js/views.js:565 +#: addons/web/static/src/xml/base.xml:480 +msgid "Fields View Get" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:573 +#, python-format +msgid "View Log (%s)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:600 +#, python-format +msgid "Model %s fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:610 +#: addons/web/static/src/xml/base.xml:482 +msgid "Manage Views" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:611 +msgid "Could not find current view declaration" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:805 +msgid "Translate" +msgstr "ભાષાંતર" + +#. openerp-web +#: addons/web/static/src/js/views.js:807 +msgid "Technical translation" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:811 +msgid "Other Options" +msgstr "અન્ય વિકલ્પો" + +#. openerp-web +#: addons/web/static/src/js/views.js:814 +#: addons/web/static/src/xml/base.xml:1736 +msgid "Import" +msgstr "આયાત" + +#. openerp-web +#: addons/web/static/src/js/views.js:817 +#: addons/web/static/src/xml/base.xml:1606 +msgid "Export" +msgstr "નિકાસ" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Reports" +msgstr "અહેવાલો" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Actions" +msgstr "ક્રિયાઓ" + +#. openerp-web +#: addons/web/static/src/js/views.js:825 +msgid "Links" +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:919 +msgid "You must choose at least one record." +msgstr "" + +#. openerp-web +#: addons/web/static/src/js/views.js:920 +msgid "Warning" +msgstr "ચેતવણી" + +#. openerp-web +#: addons/web/static/src/js/views.js:957 +msgid "Translations" +msgstr "ભાષાંતરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +msgid "Powered by" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:44 +#: addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:1813 +msgid "OpenERP" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:52 +msgid "Loading..." +msgstr "લાવી રહ્યા છીએ..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:61 +msgid "CREATE DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:68 +#: addons/web/static/src/xml/base.xml:211 +msgid "Master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:72 +#: addons/web/static/src/xml/base.xml:191 +msgid "New database name:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:77 +msgid "Load Demonstration data:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:81 +msgid "Default language:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:91 +msgid "Admin password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:95 +msgid "Confirm password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:109 +msgid "DROP DATABASE" +msgstr "" + +#. 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 "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:128 +#: addons/web/static/src/xml/base.xml:162 +#: addons/web/static/src/xml/base.xml:187 +msgid "Master Password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:132 +#: addons/web/static/src/xml/base.xml:328 +msgid "Drop" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:143 +msgid "BACKUP DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:166 +#: addons/web/static/src/xml/base.xml:329 +msgid "Backup" +msgstr "બેકઅપ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:175 +msgid "RESTORE DATABASE" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:182 +msgid "File:" +msgstr "ફાઇલ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:195 +#: addons/web/static/src/xml/base.xml:330 +msgid "Restore" +msgstr "પુનઃસ્થાપિત કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:204 +msgid "CHANGE MASTER PASSWORD" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:216 +msgid "New master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:221 +msgid "Confirm new master password:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "" +"Your version of OpenERP is unsupported. Support & maintenance services are " +"available here:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:251 +msgid "OpenERP Entreprise" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:256 +msgid "OpenERP Enterprise Contract." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:257 +msgid "Your report will be sent to the OpenERP Enterprise team." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:259 +msgid "Summary:" +msgstr "સાર:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:263 +msgid "Description:" +msgstr "વર્ણન:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:267 +msgid "What you did:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:297 +msgid "Invalid username or password" +msgstr "અયોગ્ય વપરાશકર્તા નામ અથવા પાસવર્ડ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:306 +msgid "Username" +msgstr "વપરાશકર્તા નામ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:308 +#: addons/web/static/src/xml/base.xml:331 +msgid "Password" +msgstr "પાસવર્ડ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:310 +msgid "Log in" +msgstr "લોગ ઇન" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:314 +msgid "Manage Databases" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:332 +msgid "Back to Login" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:353 +msgid "Home" +msgstr "હોમ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:363 +msgid "LOGOUT" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:388 +msgid "Fold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:389 +msgid "Unfold menu" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:454 +msgid "Hide this tip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:455 +msgid "Disable all tips" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:463 +msgid "Add / Remove Shortcut..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:471 +msgid "More…" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:477 +msgid "Debug View#" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:478 +msgid "View Log (perm_read)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:479 +msgid "View Fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:483 +msgid "View" +msgstr "દેખાવ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:484 +msgid "Edit SearchView" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:485 +msgid "Edit Action" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:486 +msgid "Edit Workflow" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:491 +msgid "ID:" +msgstr "ID:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:494 +msgid "XML ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:497 +msgid "Creation User:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:500 +msgid "Creation Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:503 +msgid "Latest Modification by:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:506 +msgid "Latest Modification Date:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:542 +msgid "Field" +msgstr "ક્ષેત્ર" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:632 +#: addons/web/static/src/xml/base.xml:758 +#: addons/web/static/src/xml/base.xml:1708 +msgid "Delete" +msgstr "રદ્દ કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:757 +msgid "Duplicate" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:775 +msgid "Add attachment" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:801 +msgid "Default:" +msgstr "મૂળભૂત:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:818 +msgid "Condition:" +msgstr "શરત:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:837 +msgid "Only you" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:844 +msgid "All users" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:851 +msgid "Unhandled widget" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:900 +msgid "Notebook Page \"" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:905 +#: addons/web/static/src/xml/base.xml:964 +msgid "Modifiers:" +msgstr "સુધારકો:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:931 +msgid "(nolabel)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:936 +msgid "Field:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:940 +msgid "Object:" +msgstr "વસ્તુ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:944 +msgid "Type:" +msgstr "પ્રકાર:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:948 +msgid "Widget:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:952 +msgid "Size:" +msgstr "કદ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:956 +msgid "Context:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:960 +msgid "Domain:" +msgstr "ડોમેઇન:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:968 +msgid "Change default:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:972 +msgid "On change:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:976 +msgid "Relation:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:980 +msgid "Selection:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1020 +msgid "Send an e-mail with your default e-mail client" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1034 +msgid "Open this resource" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1056 +msgid "Select date" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1090 +msgid "Open..." +msgstr "ખોલો..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1091 +msgid "Create..." +msgstr "બનાવો..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1092 +msgid "Search..." +msgstr "શોધ..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1095 +msgid "..." +msgstr "..." + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1155 +#: addons/web/static/src/xml/base.xml:1198 +msgid "Set Image" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1163 +#: addons/web/static/src/xml/base.xml:1213 +#: addons/web/static/src/xml/base.xml:1215 +#: addons/web/static/src/xml/base.xml:1272 +msgid "Clear" +msgstr "સાફ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1172 +#: addons/web/static/src/xml/base.xml:1223 +msgid "Uploading ..." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1200 +#: addons/web/static/src/xml/base.xml:1495 +msgid "Select" +msgstr "પસંદ કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1207 +#: addons/web/static/src/xml/base.xml:1209 +msgid "Save As" +msgstr "આ રીતે સંગ્રહિત કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1238 +msgid "Button" +msgstr "બટન" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1241 +msgid "(no string)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1248 +msgid "Special:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1253 +msgid "Button Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1257 +msgid "Method:" +msgstr "પદ્દતિ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1261 +msgid "Action ID:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1271 +msgid "Search" +msgstr "શોધો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1279 +msgid "Filters" +msgstr "ફિલ્ટરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1280 +msgid "-- Filters --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1289 +msgid "-- Actions --" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1290 +msgid "Add Advanced Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1291 +msgid "Save Filter" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1293 +msgid "Manage Filters" +msgstr "ફિલ્ટરો ને સંચાલિત કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1298 +msgid "Filter Name:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1300 +msgid "(Any existing filter with the same name will be replaced)" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1305 +msgid "Select Dashboard to add this filter to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1309 +msgid "Title of new Dashboard item:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1416 +msgid "Advanced Filters" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1426 +msgid "Any of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1427 +msgid "All the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1428 +msgid "None of the following conditions must match" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1435 +msgid "Add condition" +msgstr "શરત ઉમેરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1436 +msgid "and" +msgstr "અને" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1503 +msgid "Save & New" +msgstr "સંગ્રહો અને નવું" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1504 +msgid "Save & Close" +msgstr "સંગ્રહો અને બંધ કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1611 +msgid "" +"This wizard will export all data that matches the current search criteria to " +"a CSV file.\n" +" You can export all data or only the fields that can be " +"reimported after modification." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1618 +msgid "Export Type:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1620 +msgid "Import Compatible Export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1621 +msgid "Export all Data" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1624 +msgid "Export Formats" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1630 +msgid "Available fields" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1632 +msgid "Fields to export" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1634 +msgid "Save fields list" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1648 +msgid "Remove All" +msgstr "બધું દૂર કરો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1660 +msgid "Name" +msgstr "નામ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1693 +msgid "Save as:" +msgstr "આ રૂપમાં સાચવો:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1700 +msgid "Saved exports:" +msgstr "સચવાયેલા નિકાસ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1714 +msgid "Old Password:" +msgstr "જૂનું પાસવર્ડ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1719 +msgid "New Password:" +msgstr "નવો પાસવર્ડ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1724 +msgid "Confirm Password:" +msgstr "પાસવર્ડની પુષ્ટિ કરો:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1742 +msgid "1. Import a .CSV file" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1743 +msgid "" +"Select a .CSV file to import. If you need a sample of file to import,\n" +" you should use the export tool with the \"Import Compatible\" option." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1747 +msgid "CSV File:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1750 +msgid "2. Check your file format" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1753 +msgid "Import Options" +msgstr "આયાત વિકલ્પો" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1757 +msgid "Does your file have titles?" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1763 +msgid "Separator:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1765 +msgid "Delimiter:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1769 +msgid "Encoding:" +msgstr "એનકોડીંગ:" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1772 +msgid "UTF-8" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1773 +msgid "Latin 1" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "Lines to skip" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1776 +msgid "" +"For use if CSV files have titles on multiple lines, skips more than a single " +"line during import" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1803 +msgid "The import failed due to:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1805 +msgid "Here is a preview of the file we could not import:" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1812 +msgid "Activate the developper mode" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1814 +msgid "Version" +msgstr "આવૃત્તિ" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1815 +msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved." +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1816 +msgid "OpenERP is a trademark of the" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1817 +msgid "OpenERP SA Company" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1819 +msgid "Licenced under the terms of" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1820 +msgid "GNU Affero General Public License" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1822 +msgid "For more information visit" +msgstr "" + +#. openerp-web +#: addons/web/static/src/xml/base.xml:1823 +msgid "OpenERP.com" +msgstr "" diff --git a/addons/web/i18n/nl.po b/addons/web/i18n/nl.po index 78cd13ffd97..d2f97026967 100644 --- a/addons/web/i18n/nl.po +++ b/addons/web/i18n/nl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-14 15:27+0100\n" -"PO-Revision-Date: 2012-04-02 13:15+0000\n" +"PO-Revision-Date: 2012-04-16 12:21+0000\n" "Last-Translator: Erwin <Unknown>\n" "Language-Team: Dutch <nl@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-04-03 05:16+0000\n" -"X-Generator: Launchpad (build 15052)\n" +"X-Launchpad-Export-Date: 2012-04-17 05:23+0000\n" +"X-Generator: Launchpad (build 15099)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:172 @@ -53,7 +53,7 @@ msgstr "Ongeldige database naam" #. openerp-web #: addons/web/static/src/js/chrome.js:483 msgid "Backed" -msgstr "" +msgstr "Back-up gereed" #. openerp-web #: addons/web/static/src/js/chrome.js:484 diff --git a/addons/web/static/lib/backbone/backbone.js b/addons/web/static/lib/backbone/backbone.js new file mode 100644 index 00000000000..7c56f42363c --- /dev/null +++ b/addons/web/static/lib/backbone/backbone.js @@ -0,0 +1,1290 @@ +// Backbone.js 0.9.1 + +// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org + +(function(){ + + // Initial Setup + // ------------- + + // Save a reference to the global object (`window` in the browser, `global` + // on the server). + var root = this; + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create a local reference to slice/splice. + var slice = Array.prototype.slice; + var splice = Array.prototype.splice; + + // The top-level namespace. All public Backbone classes and modules will + // be attached to this. Exported for both CommonJS and the browser. + var Backbone; + if (typeof exports !== 'undefined') { + Backbone = exports; + } else { + Backbone = root.Backbone = {}; + } + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '0.9.1'; + + // Require Underscore, if we're on the server, and it's not already present. + var _ = root._; + if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); + + // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. + var $ = root.jQuery || root.Zepto || root.ender; + + // Set the JavaScript library that will be used for DOM manipulation and + // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, + // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an + // alternate JavaScript library (or a mock library for testing your views + // outside of a browser). + Backbone.setDomLibrary = function(lib) { + $ = lib; + }; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // ----------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback functions + // to an event; trigger`-ing an event fires all callbacks in succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + Backbone.Events = { + + // Bind an event, specified by a string name, `ev`, to a `callback` + // function. Passing `"all"` will bind the callback to all events fired. + on: function(events, callback, context) { + var ev; + events = events.split(/\s+/); + var calls = this._callbacks || (this._callbacks = {}); + while (ev = events.shift()) { + // Create an immutable callback list, allowing traversal during + // modification. The tail is an empty object that will always be used + // as the next node. + var list = calls[ev] || (calls[ev] = {}); + var tail = list.tail || (list.tail = list.next = {}); + tail.callback = callback; + tail.context = context; + list.tail = tail.next = {}; + } + return this; + }, + + // Remove one or many callbacks. If `context` is null, removes all callbacks + // with that function. If `callback` is null, removes all callbacks for the + // event. If `ev` is null, removes all bound callbacks for all events. + off: function(events, callback, context) { + var ev, calls, node; + if (!events) { + delete this._callbacks; + } else if (calls = this._callbacks) { + events = events.split(/\s+/); + while (ev = events.shift()) { + node = calls[ev]; + delete calls[ev]; + if (!callback || !node) continue; + // Create a new list, omitting the indicated event/context pairs. + while ((node = node.next) && node.next) { + if (node.callback === callback && + (!context || node.context === context)) continue; + this.on(ev, node.callback, node.context); + } + } + } + return this; + }, + + // Trigger an event, firing all bound callbacks. Callbacks are passed the + // same arguments as `trigger` is, apart from the event name. + // Listening for `"all"` passes the true event name as the first argument. + trigger: function(events) { + var event, node, calls, tail, args, all, rest; + if (!(calls = this._callbacks)) return this; + all = calls['all']; + (events = events.split(/\s+/)).push(null); + // Save references to the current heads & tails. + while (event = events.shift()) { + if (all) events.push({next: all.next, tail: all.tail, event: event}); + if (!(node = calls[event])) continue; + events.push({next: node.next, tail: node.tail}); + } + // Traverse each list, stopping when the saved tail is reached. + rest = slice.call(arguments, 1); + while (node = events.pop()) { + tail = node.tail; + args = node.event ? [node.event].concat(rest) : rest; + while ((node = node.next) !== tail) { + node.callback.apply(node.context || this, args); + } + } + return this; + } + + }; + + // Aliases for backwards compatibility. + Backbone.Events.bind = Backbone.Events.on; + Backbone.Events.unbind = Backbone.Events.off; + + // Backbone.Model + // -------------- + + // Create a new model, with defined attributes. A client id (`cid`) + // is automatically generated and assigned for you. + Backbone.Model = function(attributes, options) { + var defaults; + attributes || (attributes = {}); + if (options && options.parse) attributes = this.parse(attributes); + if (defaults = getValue(this, 'defaults')) { + attributes = _.extend({}, defaults, attributes); + } + if (options && options.collection) this.collection = options.collection; + this.attributes = {}; + this._escapedAttributes = {}; + this.cid = _.uniqueId('c'); + if (!this.set(attributes, {silent: true})) { + throw new Error("Can't create an invalid model"); + } + delete this._changed; + this._previousAttributes = _.clone(this.attributes); + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Backbone.Model.prototype, Backbone.Events, { + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function() { + return _.clone(this.attributes); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + var html; + if (html = this._escapedAttributes[attr]) return html; + var val = this.attributes[attr]; + return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.attributes[attr] != null; + }, + + // Set a hash of model attributes on the object, firing `"change"` unless + // you choose to silence it. + set: function(key, value, options) { + var attrs, attr, val; + if (_.isObject(key) || key == null) { + attrs = key; + options = value; + } else { + attrs = {}; + attrs[key] = value; + } + + // Extract attributes and options. + options || (options = {}); + if (!attrs) return this; + if (attrs instanceof Backbone.Model) attrs = attrs.attributes; + if (options.unset) for (attr in attrs) attrs[attr] = void 0; + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + var now = this.attributes; + var escaped = this._escapedAttributes; + var prev = this._previousAttributes || {}; + var alreadySetting = this._setting; + this._changed || (this._changed = {}); + this._setting = true; + + // Update attributes. + for (attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(now[attr], val)) delete escaped[attr]; + options.unset ? delete now[attr] : now[attr] = val; + if (this._changing && !_.isEqual(this._changed[attr], val)) { + this.trigger('change:' + attr, this, val, options); + this._moreChanges = true; + } + delete this._changed[attr]; + if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { + this._changed[attr] = val; + } + } + + // Fire the `"change"` events, if the model has been changed. + if (!alreadySetting) { + if (!options.silent && this.hasChanged()) this.change(options); + this._setting = false; + } + return this; + }, + + // Remove an attribute from the model, firing `"change"` unless you choose + // to silence it. `unset` is a noop if the attribute doesn't exist. + unset: function(attr, options) { + (options || (options = {})).unset = true; + return this.set(attr, null, options); + }, + + // Clear all attributes on the model, firing `"change"` unless you choose + // to silence it. + clear: function(options) { + (options || (options = {})).unset = true; + return this.set(_.clone(this.attributes), options); + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overriden, + // triggering a `"change"` event. + fetch: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + if (!model.set(model.parse(resp, xhr), options)) return false; + if (success) success(model, resp); + }; + options.error = Backbone.wrapError(options.error, model, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, value, options) { + var attrs, current; + if (_.isObject(key) || key == null) { + attrs = key; + options = value; + } else { + attrs = {}; + attrs[key] = value; + } + + options = options ? _.clone(options) : {}; + if (options.wait) current = _.clone(this.attributes); + var silentOptions = _.extend({}, options, {silent: true}); + if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) { + return false; + } + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + var serverAttrs = model.parse(resp, xhr); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (!model.set(serverAttrs, options)) return false; + if (success) { + success(model, resp); + } else { + model.trigger('sync', model, resp, options); + } + }; + options.error = Backbone.wrapError(options.error, model, options); + var method = this.isNew() ? 'create' : 'update'; + var xhr = (this.sync || Backbone.sync).call(this, method, this, options); + if (options.wait) this.set(current, silentOptions); + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + + var triggerDestroy = function() { + model.trigger('destroy', model, model.collection, options); + }; + + if (this.isNew()) return triggerDestroy(); + options.success = function(resp) { + if (options.wait) triggerDestroy(); + if (success) { + success(model, resp); + } else { + model.trigger('sync', model, resp, options); + } + }; + options.error = Backbone.wrapError(options.error, model, options); + var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options); + if (!options.wait) triggerDestroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = getValue(this.collection, 'url') || getValue(this, 'urlRoot') || urlError(); + if (this.isNew()) return base; + return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, xhr) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return this.id == null; + }, + + // Call this method to manually fire a `"change"` event for this model and + // a `"change:attribute"` event for each changed attribute. + // Calling this will cause all objects observing the model to update. + change: function(options) { + if (this._changing || !this.hasChanged()) return this; + this._changing = true; + this._moreChanges = true; + for (var attr in this._changed) { + this.trigger('change:' + attr, this, this._changed[attr], options); + } + while (this._moreChanges) { + this._moreChanges = false; + this.trigger('change', this, options); + } + this._previousAttributes = _.clone(this.attributes); + delete this._changed; + this._changing = false; + return this; + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (!arguments.length) return !_.isEmpty(this._changed); + return this._changed && _.has(this._changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this._changed) : false; + var val, changed = false, old = this._previousAttributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (!arguments.length || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Check if the model is currently in a valid state. It's only possible to + // get into an *invalid* state if you're using silent changes. + isValid: function() { + return !this.validate(this.attributes); + }, + + // Run validation against a set of incoming attributes, returning `true` + // if all is well. If a specific `error` callback has been passed, + // call that instead of firing the general `"error"` event. + _validate: function(attrs, options) { + if (options.silent || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validate(attrs, options); + if (!error) return true; + if (options && options.error) { + options.error(this, error, options); + } else { + this.trigger('error', this, error, options); + } + return false; + } + + }); + + // Backbone.Collection + // ------------------- + + // Provides a standard collection class for our sets of models, ordered + // or unordered. If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.comparator) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, {silent: true, parse: options.parse}); + }; + + // Define the Collection's inheritable methods. + _.extend(Backbone.Collection.prototype, Backbone.Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Backbone.Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function() { + return this.map(function(model){ return model.toJSON(); }); + }, + + // Add a model, or list of models to the set. Pass **silent** to avoid + // firing the `add` event for every new model. + add: function(models, options) { + var i, index, length, model, cid, id, cids = {}, ids = {}; + options || (options = {}); + models = _.isArray(models) ? models.slice() : [models]; + + // Begin by turning bare objects into model references, and preventing + // invalid models or duplicate models from being added. + for (i = 0, length = models.length; i < length; i++) { + if (!(model = models[i] = this._prepareModel(models[i], options))) { + throw new Error("Can't add an invalid model to a collection"); + } + if (cids[cid = model.cid] || this._byCid[cid] || + (((id = model.id) != null) && (ids[id] || this._byId[id]))) { + throw new Error("Can't add the same model to a collection twice"); + } + cids[cid] = ids[id] = model; + } + + // Listen to added models' events, and index models for lookup by + // `id` and by `cid`. + for (i = 0; i < length; i++) { + (model = models[i]).on('all', this._onModelEvent, this); + this._byCid[model.cid] = model; + if (model.id != null) this._byId[model.id] = model; + } + + // Insert models into the collection, re-sorting if needed, and triggering + // `add` events unless silenced. + this.length += length; + index = options.at != null ? options.at : this.models.length; + splice.apply(this.models, [index, 0].concat(models)); + if (this.comparator) this.sort({silent: true}); + if (options.silent) return this; + for (i = 0, length = this.models.length; i < length; i++) { + if (!cids[(model = this.models[i]).cid]) continue; + options.index = i; + model.trigger('add', model, this, options); + } + return this; + }, + + // Remove a model, or a list of models from the set. Pass silent to avoid + // firing the `remove` event for every model removed. + remove: function(models, options) { + var i, l, index, model; + options || (options = {}); + models = _.isArray(models) ? models.slice() : [models]; + for (i = 0, l = models.length; i < l; i++) { + model = this.getByCid(models[i]) || this.get(models[i]); + if (!model) continue; + delete this._byId[model.id]; + delete this._byCid[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model); + } + return this; + }, + + // Get a model from the set by id. + get: function(id) { + if (id == null) return null; + return this._byId[id.id != null ? id.id : id]; + }, + + // Get a model from the set by client id. + getByCid: function(cid) { + return cid && this._byCid[cid.cid || cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + options || (options = {}); + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + var boundComparator = _.bind(this.comparator, this); + if (this.comparator.length == 1) { + this.models = this.sortBy(boundComparator); + } else { + this.models.sort(boundComparator); + } + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.map(this.models, function(model){ return model.get(attr); }); + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any `add` or `remove` events. Fires `reset` when finished. + reset: function(models, options) { + models || (models = []); + options || (options = {}); + for (var i = 0, l = this.models.length; i < l; i++) { + this._removeReference(this.models[i]); + } + this._reset(); + this.add(models, {silent: true, parse: options.parse}); + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `add: true` is passed, appends the + // models to the collection instead of resetting. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === undefined) options.parse = true; + var collection = this; + var success = options.success; + options.success = function(resp, status, xhr) { + collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); + if (success) success(collection, resp); + }; + options.error = Backbone.wrapError(options.error, collection, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + var coll = this; + options = options ? _.clone(options) : {}; + model = this._prepareModel(model, options); + if (!model) return false; + if (!options.wait) coll.add(model, options); + var success = options.success; + options.success = function(nextModel, resp, xhr) { + if (options.wait) coll.add(nextModel, options); + if (success) { + success(nextModel, resp); + } else { + nextModel.trigger('sync', model, resp, options); + } + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, xhr) { + return resp; + }, + + // Proxy to _'s chain. Can't be proxied the same way the rest of the + // underscore methods are proxied because it relies on the underscore + // constructor. + chain: function () { + return _(this.models).chain(); + }, + + // Reset all internal state. Called when the collection is reset. + _reset: function(options) { + this.length = 0; + this.models = []; + this._byId = {}; + this._byCid = {}; + }, + + // Prepare a model or hash of attributes to be added to this collection. + _prepareModel: function(model, options) { + if (!(model instanceof Backbone.Model)) { + var attrs = model; + options.collection = this; + model = new this.model(attrs, options); + if (!model._validate(model.attributes, options)) model = false; + } else if (!model.collection) { + model.collection = this; + } + return model; + }, + + // Internal method to remove a model's ties to a collection. + _removeReference: function(model) { + if (this == model.collection) { + delete model.collection; + } + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(ev, model, collection, options) { + if ((ev == 'add' || ev == 'remove') && collection != this) return; + if (ev == 'destroy') { + this.remove(model, options); + } + if (model && ev === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', + 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', + 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', + 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', + 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Backbone.Collection.prototype[method] = function() { + return _[method].apply(_, [this.models].concat(_.toArray(arguments))); + }; + }); + + // Backbone.Router + // ------------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var namedParam = /:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Backbone.Router.prototype, Backbone.Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + Backbone.history || (Backbone.history = new Backbone.History); + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (!callback) callback = this[name]; + Backbone.history.route(route, _.bind(function(fragment) { + var args = this._extractParameters(route, fragment); + callback && callback.apply(this, args); + this.trigger.apply(this, ['route:' + name].concat(args)); + Backbone.history.trigger('route', this, name, args); + }, this)); + return this; + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + var routes = []; + for (var route in this.routes) { + routes.unshift([route, this.routes[route]]); + } + for (var i = 0, l = routes.length; i < l; i++) { + this.route(routes[i][0], routes[i][1], this[routes[i][1]]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(namedParam, '([^\/]+)') + .replace(splatParam, '(.*?)'); + return new RegExp('^' + route + '$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted parameters. + _extractParameters: function(route, fragment) { + return route.exec(fragment).slice(1); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on URL fragments. If the + // browser does not support `onhashchange`, falls back to polling. + Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + }; + + // Cached regex for cleaning leading hashes and slashes . + var routeStripper = /^[#\/]/; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Has the history handling already been started? + var historyStarted = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(Backbone.History.prototype, Backbone.Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || forcePushState) { + fragment = window.location.pathname; + var search = window.location.search; + if (search) fragment += search; + } else { + fragment = window.location.hash; + } + } + fragment = decodeURIComponent(fragment); + if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length); + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + if (historyStarted) throw new Error("Backbone.history has already been started"); + this.options = _.extend({}, {root: '/'}, this.options, options); + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + if (oldIE) { + this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow; + this.navigate(fragment); + } + + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._hasPushState) { + $(window).bind('popstate', this.checkUrl); + } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { + $(window).bind('hashchange', this.checkUrl); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); + } + + // Determine if we need to change the base url, for a pushState link + // opened by a non-pushState browser. + this.fragment = fragment; + historyStarted = true; + var loc = window.location; + var atRoot = loc.pathname == this.options.root; + + // If we've started off with a route from a `pushState`-enabled browser, + // but we're currently in a browser that doesn't support it... + if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) { + this.fragment = this.getFragment(null, true); + window.location.replace(this.options.root + '#' + this.fragment); + // Return immediately as browser will do redirect to new url + return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) { + this.fragment = loc.hash.replace(routeStripper, ''); + window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); + } + + if (!this.options.silent) { + return this.loadUrl(); + } + }, + + // Disable Backbone.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop: function() { + $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl); + clearInterval(this._checkUrlInterval); + historyStarted = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route: function(route, callback) { + this.handlers.unshift({route: route, callback: callback}); + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl: function(e) { + var current = this.getFragment(); + if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash); + if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false; + if (this.iframe) this.navigate(current); + this.loadUrl() || this.loadUrl(window.location.hash); + }, + + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl: function(fragmentOverride) { + var fragment = this.fragment = this.getFragment(fragmentOverride); + var matched = _.any(this.handlers, function(handler) { + if (handler.route.test(fragment)) { + handler.callback(fragment); + return true; + } + }); + return matched; + }, + + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you which to modify the current URL without adding an entry to the history. + navigate: function(fragment, options) { + if (!historyStarted) return false; + if (!options || options === true) options = {trigger: options}; + var frag = (fragment || '').replace(routeStripper, ''); + if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return; + + // If pushState is available, we use it to set the fragment as a real URL. + if (this._hasPushState) { + if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag; + this.fragment = frag; + window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag); + + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this.fragment = frag; + this._updateHash(window.location, frag, options.replace); + if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) { + // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change. + // When replace is true, we don't want this. + if(!options.replace) this.iframe.document.open().close(); + this._updateHash(this.iframe.location, frag, options.replace); + } + + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + window.location.assign(this.options.root + fragment); + } + if (options.trigger) this.loadUrl(fragment); + }, + + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash: function(location, fragment, replace) { + if (replace) { + location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment); + } else { + location.hash = fragment; + } + } + }); + + // Backbone.View + // ------------- + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + this._configure(options || {}); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + // Cached regex to split keys for `delegate`. + var eventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(Backbone.View.prototype, Backbone.Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be prefered to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view from the DOM. Note that the view isn't present in the + // DOM by default, so calling this method may be a no-op. + remove: function() { + this.$el.remove(); + return this; + }, + + // For small amounts of DOM Elements, where a full-blown template isn't + // needed, use **make** to manufacture elements, one at a time. + // + // var el = this.make('li', {'class': 'row'}, this.model.escape('title')); + // + make: function(tagName, attributes, content) { + var el = document.createElement(tagName); + if (attributes) $(el).attr(attributes); + if (content) $(el).html(content); + return el; + }, + + // Change the view's element (`this.el` property), including event + // re-delegation. + setElement: function(element, delegate) { + this.$el = $(element); + this.el = this.$el[0]; + if (delegate !== false) this.delegateEvents(); + return this; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save' + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + // This only works for delegate-able events: not `focus`, `blur`, and + // not `change`, `submit`, and `reset` in Internet Explorer. + delegateEvents: function(events) { + if (!(events || (events = getValue(this, 'events')))) return; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[events[key]]; + if (!method) throw new Error('Event "' + events[key] + '" does not exist'); + var match = key.match(eventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, this); + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.bind(eventName, method); + } else { + this.$el.delegate(selector, eventName, method); + } + } + }, + + // Clears all callbacks previously bound to the view with `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + this.$el.unbind('.delegateEvents' + this.cid); + }, + + // Performs the initial configuration of a View with a set of options. + // Keys with special meaning *(model, collection, id, className)*, are + // attached directly to the view. + _configure: function(options) { + if (this.options) options = _.extend({}, this.options, options); + for (var i = 0, l = viewOptions.length; i < l; i++) { + var attr = viewOptions[i]; + if (options[attr]) this[attr] = options[attr]; + } + this.options = options; + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = getValue(this, 'attributes') || {}; + if (this.id) attrs.id = this.id; + if (this.className) attrs['class'] = this.className; + this.setElement(this.make(this.tagName, attrs), false); + } else { + this.setElement(this.el, false); + } + } + + }); + + // The self-propagating extend function that Backbone classes use. + var extend = function (protoProps, classProps) { + var child = inherits(this, protoProps, classProps); + child.extend = this.extend; + return child; + }; + + // Set up inheritance for the model, collection, and view. + Backbone.Model.extend = Backbone.Collection.extend = + Backbone.Router.extend = Backbone.View.extend = extend; + + // Backbone.sync + // ------------- + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = getValue(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (!options.data && model && (method == 'create' || method == 'update')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(model.toJSON()); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (Backbone.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (Backbone.emulateHTTP) { + if (type === 'PUT' || type === 'DELETE') { + if (Backbone.emulateJSON) params.data._method = type; + params.type = 'POST'; + params.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + }; + } + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !Backbone.emulateJSON) { + params.processData = false; + } + + // Make the request, allowing the user to override any Ajax options. + return $.ajax(_.extend(params, options)); + }; + + // Wrap an optional error callback with a fallback error event. + Backbone.wrapError = function(onError, originalModel, options) { + return function(model, resp) { + resp = model === originalModel ? resp : model; + if (onError) { + onError(originalModel, resp, options); + } else { + originalModel.trigger('error', originalModel, resp, options); + } + }; + }; + + // Helpers + // ------- + + // Shared empty constructor function to aid in prototype-chain creation. + var ctor = function(){}; + + // Helper function to correctly set up the prototype chain, for subclasses. + // Similar to `goog.inherits`, but uses a hash of prototype properties and + // class properties to be extended. + var inherits = function(parent, protoProps, staticProps) { + var child; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent's constructor. + if (protoProps && protoProps.hasOwnProperty('constructor')) { + child = protoProps.constructor; + } else { + child = function(){ parent.apply(this, arguments); }; + } + + // Inherit class (static) properties from parent. + _.extend(child, parent); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function. + ctor.prototype = parent.prototype; + child.prototype = new ctor(); + + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) _.extend(child.prototype, protoProps); + + // Add static properties to the constructor function, if supplied. + if (staticProps) _.extend(child, staticProps); + + // Correctly set child's `prototype.constructor`. + child.prototype.constructor = child; + + // Set a convenience property in case the parent's prototype is needed later. + child.__super__ = parent.prototype; + + return child; + }; + + // Helper function to get a value from a Backbone object as a property + // or as a function. + var getValue = function(object, prop) { + if (!(object && object[prop])) return null; + return _.isFunction(object[prop]) ? object[prop]() : object[prop]; + }; + + // Throw an error when a URL is needed, and none is supplied. + var urlError = function() { + throw new Error('A "url" property or function must be specified'); + }; + +}).call(this); diff --git a/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js b/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js deleted file mode 100644 index 585bc072e56..00000000000 --- a/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js +++ /dev/null @@ -1,132 +0,0 @@ -/* - Fork of some unlicensed library found somewhere, don't hesitate to - patch it directly. -*/ -(function($) { - var menu,shadow,trigger,content,hash,currentTarget; - var defaults= { - menuStyle: { - listStyle:'none', - padding:'1px', - margin:'0px', - backgroundColor:'#fff', - border:'1px solid #999', - width:'100px' - }, - itemStyle: { - margin:'0px', - color:'#000', - display:'block', - cursor:'default', - padding:'3px', - border:'1px solid #fff', - backgroundColor:'transparent' - }, - itemHoverStyle: { - border:'1px solid #0a246a', - backgroundColor:'#b6bdd2' - }, - eventPosX:'pageX', - eventPosY:'pageY', - shadow:true, - onContextMenu:null, - onShowMenu:null - }; - $.fn.contextMenu= function(id,options) { - if(!menu) { - menu=$('<div id="jqContextMenu" class="openerp"></div>').hide().css({ - position:'absolute', - zIndex:'2000' - }).appendTo('body').bind('click', function(e) { - e.stopPropagation() - }) - } - if(!shadow) { - shadow=$('<div></div>').css({ - backgroundColor:'#000', - position:'absolute', - opacity:0.2, - zIndex:499 - }).appendTo('body').hide() - } - hash=hash||[]; - hash.push({ - id:id, - menuStyle:$.extend({},defaults.menuStyle,options.menuStyle|| {}), - itemStyle:$.extend({},defaults.itemStyle,options.itemStyle|| {}), - itemHoverStyle:$.extend({},defaults.itemHoverStyle,options.itemHoverStyle|| {}), - bindings:options.bindings|| {}, - shadow:options.shadow||options.shadow===false?options.shadow:defaults.shadow, - onContextMenu:options.onContextMenu||defaults.onContextMenu, - onShowMenu:options.onShowMenu||defaults.onShowMenu, - eventPosX:options.eventPosX||defaults.eventPosX, - eventPosY:options.eventPosY||defaults.eventPosY - }); - var index=hash.length-1; - var callback = function(e) { - var bShowContext=(!!hash[index].onContextMenu)?hash[index].onContextMenu(e):true; - if(bShowContext) - display(index,this,e,options); - return false; - }; - if (!options.noRightClick) { - $(this).bind('contextmenu', callback); - } - if (options.leftClickToo || options.noRightClick) { - $(this).click(callback); - } - return this - }; - function display(index,trigger,e,options) { - var cur=hash[index]; - content=$('#'+cur.id).find('ul:first').clone(true); - content.css(cur.menuStyle).find('li').css(cur.itemStyle).hover( function() { - $(this).css(cur.itemHoverStyle) - }, function() { - $(this).css(cur.itemStyle) - }).find('img').css({ - verticalAlign:'middle', - paddingRight:'2px' - }); - menu.html(content); - if(!!cur.onShowMenu) - menu=cur.onShowMenu(e,menu); - $.each(cur.bindings, function(id,func) { - $('#'+id,menu).bind('click', function(e) { - hide(); - func(trigger,currentTarget) - }) - }); - menu.css({ - 'left':e[cur.eventPosX], - 'top':e[cur.eventPosY] - }).show(); - if(cur.shadow) - shadow.css({ - width:menu.width(), - height:menu.height(), - left:e.pageX+2, - top:e.pageY+2 - }).show(); - $(document).one('click',hide) - } - - function hide() { - menu.hide(); - shadow.hide() - } - - $.contextMenu= { - defaults: function(userDefaults) { - $.each(userDefaults, function(i,val) { - if(typeof val=='object'&&defaults[i]) { - $.extend(defaults[i],val) - } else - defaults[i]=val - }) - } - } -})(jQuery); -$( function() { - $('div.contextMenu').hide() -}); diff --git a/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js b/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js index 09dda6a3060..f36c9bdb43a 100644 --- a/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js +++ b/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js @@ -63,7 +63,7 @@ } } - $tip.css(tp).addClass('tipsy-' + gravity); + $tip.css(tp).addClass('openerp oe_tooltip tipsy-' + gravity); $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0); if (this.options.className) { $tip.addClass(maybeCall(this.options.className, this.$element[0])); diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/bootstrap.css b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/bootstrap.css new file mode 100644 index 00000000000..90e87861e4e --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/bootstrap.css @@ -0,0 +1,2476 @@ +/*! + * Bootstrap v1.4.0 + * + * Copyright 2011 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + * Date: Sun Dec 25 20:18:31 PST 2011 + */ +/* Reset.less + * Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). + * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ +html, body { + margin: 0; + padding: 0; +} +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +cite, +code, +del, +dfn, +em, +img, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +dd, +dl, +dt, +li, +ol, +ul, +fieldset, +form, +label, +legend, +button, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td { + margin: 0; + padding: 0; + border: 0; + font-weight: normal; + font-style: normal; + font-size: 100%; + line-height: 1; + font-family: inherit; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +ol, ul { + list-style: none; +} +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} +html { + overflow-y: scroll; + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted; +} +a:hover, a:active { + outline: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} +audio, canvas, video { + display: inline-block; + *display: inline; + *zoom: 1; +} +audio:not([controls]) { + display: none; +} +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; + -ms-interpolation-mode: bicubic; +} + +/* +button, +input, +select, +textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle; +} +button, input { + line-height: normal; + *overflow: visible; +} +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0; +} +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +}*/ +textarea { + overflow: auto; + vertical-align: top; +} +/* Variables.less + * Variables to customize the look and feel of Bootstrap + * ----------------------------------------------------- */ +/* Mixins.less + * Snippets of reusable CSS to develop faster and keep code readable + * ----------------------------------------------------------------- */ +/* + * Scaffolding + * Basic and global styles for generating a grid system, structural layout, and page templates + * ------------------------------------------------------------------------------------------- */ +body { + background-color: #ffffff; + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 18px; + color: #404040; +} +.container { + width: 940px; + margin-left: auto; + margin-right: auto; + zoom: 1; +} +.container:before, .container:after { + display: table; + content: ""; + zoom: 1; +} +.container:after { + clear: both; +} +.container-fluid { + position: relative; + min-width: 940px; + padding-left: 20px; + padding-right: 20px; + zoom: 1; +} +.container-fluid:before, .container-fluid:after { + display: table; + content: ""; + zoom: 1; +} +.container-fluid:after { + clear: both; +} +.container-fluid > .sidebar { + position: absolute; + top: 0; + left: 20px; + width: 220px; +} +.container-fluid > .content { + margin-left: 240px; +} +a { + color: #0069d6; + text-decoration: none; + line-height: inherit; + font-weight: inherit; +} +a:hover { + color: #00438a; + text-decoration: underline; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.row { + zoom: 1; + margin-left: -20px; +} +.row:before, .row:after { + display: table; + content: ""; + zoom: 1; +} +.row:after { + clear: both; +} +.row > [class*="span"] { + display: inline; + float: left; + margin-left: 20px; +} +.span1 { + width: 40px; +} +.span2 { + width: 100px; +} +.span3 { + width: 160px; +} +.span4 { + width: 220px; +} +.span5 { + width: 280px; +} +.span6 { + width: 340px; +} +.span7 { + width: 400px; +} +.span8 { + width: 460px; +} +.span9 { + width: 520px; +} +.span10 { + width: 580px; +} +.span11 { + width: 640px; +} +.span12 { + width: 700px; +} +.span13 { + width: 760px; +} +.span14 { + width: 820px; +} +.span15 { + width: 880px; +} +.span16 { + width: 940px; +} +.span17 { + width: 1000px; +} +.span18 { + width: 1060px; +} +.span19 { + width: 1120px; +} +.span20 { + width: 1180px; +} +.span21 { + width: 1240px; +} +.span22 { + width: 1300px; +} +.span23 { + width: 1360px; +} +.span24 { + width: 1420px; +} +.row > .offset1 { + margin-left: 80px; +} +.row > .offset2 { + margin-left: 140px; +} +.row > .offset3 { + margin-left: 200px; +} +.row > .offset4 { + margin-left: 260px; +} +.row > .offset5 { + margin-left: 320px; +} +.row > .offset6 { + margin-left: 380px; +} +.row > .offset7 { + margin-left: 440px; +} +.row > .offset8 { + margin-left: 500px; +} +.row > .offset9 { + margin-left: 560px; +} +.row > .offset10 { + margin-left: 620px; +} +.row > .offset11 { + margin-left: 680px; +} +.row > .offset12 { + margin-left: 740px; +} +.span-one-third { + width: 300px; +} +.span-two-thirds { + width: 620px; +} +.row > .offset-one-third { + margin-left: 340px; +} +.row > .offset-two-thirds { + margin-left: 660px; +} +/* Typography.less + * Headings, body text, lists, code, and more for a versatile and durable typography system + * ---------------------------------------------------------------------------------------- */ + + +p { + font-size: 13px; + font-weight: normal; + line-height: 18px; + margin-bottom: 9px; +} +p small { + font-size: 11px; + color: #bfbfbf; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: bold; + color: #404040; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + color: #bfbfbf; +} +h1 { + margin-bottom: 18px; + font-size: 30px; + line-height: 36px; +} +h1 small { + font-size: 18px; +} +h2 { + font-size: 24px; + line-height: 36px; +} +h2 small { + font-size: 14px; +} +h3, +h4, +h5, +h6 { + line-height: 36px; +} +h3 { + font-size: 18px; +} +h3 small { + font-size: 14px; +} +h4 { + font-size: 16px; +} +h4 small { + font-size: 12px; +} +h5 { + font-size: 14px; +} +h6 { + font-size: 13px; + color: #bfbfbf; + text-transform: uppercase; +} +ul, ol { + margin: 0 0 18px 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +ul { + list-style: disc; +} +ol { + list-style: decimal; +} +li { + line-height: 18px; + color: #808080; +} +ul.unstyled { + list-style: none; + margin-left: 0; +} +dl { + margin-bottom: 18px; +} +dl dt, dl dd { + line-height: 18px; +} +dl dt { + font-weight: bold; +} +dl dd { + margin-left: 9px; +} +hr { + margin: 20px 0 19px; + border: 0; + border-bottom: 1px solid #eee; +} +strong { + font-style: inherit; + font-weight: bold; +} +em { + font-style: italic; + font-weight: inherit; + line-height: inherit; +} +.muted { + color: #bfbfbf; +} +blockquote { + margin-bottom: 18px; + border-left: 5px solid #eee; + padding-left: 15px; +} +blockquote p { + font-size: 14px; + font-weight: 300; + line-height: 18px; + margin-bottom: 0; +} +blockquote small { + display: block; + font-size: 12px; + font-weight: 300; + line-height: 18px; + color: #bfbfbf; +} +blockquote small:before { + content: '\2014 \00A0'; +} +address { + display: block; + line-height: 18px; + margin-bottom: 18px; +} +code, pre { + padding: 0 3px 2px; + font-family: Monaco, Andale Mono, Courier New, monospace; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + background-color: #fee9cc; + color: rgba(0, 0, 0, 0.75); + padding: 1px 3px; +} +pre { + background-color: #f5f5f5; + display: block; + padding: 8.5px; + margin: 0 0 18px; + line-height: 18px; + font-size: 12px; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} +/* Forms.less + * Base styles for various input types, form layouts, and states + * ------------------------------------------------------------- */ +form { + margin-bottom: 18px; +} +fieldset { + margin-bottom: 18px; + padding-top: 18px; +} +fieldset legend { + display: block; + padding-left: 150px; + font-size: 19.5px; + line-height: 1; + color: #404040; + *padding: 0 0 5px 145px; + /* IE6-7 */ + + *line-height: 1.5; + /* IE6-7 */ + +} +form .clearfix { + margin-bottom: 18px; + zoom: 1; +} +form .clearfix:before, form .clearfix:after { + display: table; + content: ""; + zoom: 1; +} +form .clearfix:after { + clear: both; +} + +/* +label, +input, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: normal; +} +label { + padding-top: 6px; + font-size: 13px; + line-height: 18px; + float: left; + width: 130px; + text-align: right; + color: #404040; +} +form .input { + margin-left: 150px; +} +input[type=checkbox], input[type=radio] { + cursor: pointer; +} +input, +textarea, +select, +.uneditable-input { + display: inline-block; + width: 210px; + height: 18px; + padding: 4px; + font-size: 13px; + line-height: 18px; + color: #808080; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +select { + padding: initial; +} +input[type=checkbox], input[type=radio] { + width: auto; + height: auto; + padding: 0; + margin: 3px 0; + *margin-top: 0; + + line-height: normal; + border: none; +} +input[type=file] { + background-color: #ffffff; + padding: initial; + border: initial; + line-height: initial; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +input[type=button], input[type=reset], input[type=submit] { + width: auto; + height: auto; +} +select, input[type=file] { + height: 27px; + *height: auto; + line-height: 27px; + *margin-top: 4px; + +} +*/ +select[multiple] { + height: inherit; + background-color: #ffffff; +} +textarea { + height: auto; +} +.uneditable-input { + background-color: #ffffff; + display: block; + border-color: #eee; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +:-moz-placeholder { + color: #bfbfbf; +} +::-webkit-input-placeholder { + color: #bfbfbf; +} +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; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); +} +input:focus, textarea:focus { + outline: 0; + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + -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 { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: 1px dotted #666; +} +form .clearfix.error > label, form .clearfix.error .help-block, form .clearfix.error .help-inline { + color: #b94a48; +} +form .clearfix.error input, form .clearfix.error textarea { + color: #b94a48; + border-color: #ee5f5b; +} +form .clearfix.error input:focus, form .clearfix.error textarea:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} +form .clearfix.error .input-prepend .add-on, form .clearfix.error .input-append .add-on { + color: #b94a48; + background-color: #fce6e6; + border-color: #b94a48; +} +form .clearfix.warning > label, form .clearfix.warning .help-block, form .clearfix.warning .help-inline { + color: #c09853; +} +form .clearfix.warning input, form .clearfix.warning textarea { + color: #c09853; + border-color: #ccae64; +} +form .clearfix.warning input:focus, form .clearfix.warning textarea:focus { + border-color: #be9a3f; + -webkit-box-shadow: 0 0 6px #e5d6b1; + -moz-box-shadow: 0 0 6px #e5d6b1; + box-shadow: 0 0 6px #e5d6b1; +} +form .clearfix.warning .input-prepend .add-on, form .clearfix.warning .input-append .add-on { + color: #c09853; + background-color: #d2b877; + border-color: #c09853; +} +form .clearfix.success > label, form .clearfix.success .help-block, form .clearfix.success .help-inline { + color: #468847; +} +form .clearfix.success input, form .clearfix.success textarea { + color: #468847; + border-color: #57a957; +} +form .clearfix.success input:focus, form .clearfix.success textarea:focus { + border-color: #458845; + -webkit-box-shadow: 0 0 6px #9acc9a; + -moz-box-shadow: 0 0 6px #9acc9a; + box-shadow: 0 0 6px #9acc9a; +} +form .clearfix.success .input-prepend .add-on, form .clearfix.success .input-append .add-on { + color: #468847; + background-color: #bcddbc; + border-color: #468847; +} +.input-mini, +input.mini, +textarea.mini, +select.mini { + width: 60px; +} +.input-small, +input.small, +textarea.small, +select.small { + width: 90px; +} +.input-medium, +input.medium, +textarea.medium, +select.medium { + width: 150px; +} +.input-large, +input.large, +textarea.large, +select.large { + width: 210px; +} +.input-xlarge, +input.xlarge, +textarea.xlarge, +select.xlarge { + width: 270px; +} +.input-xxlarge, +input.xxlarge, +textarea.xxlarge, +select.xxlarge { + width: 530px; +} +textarea.xxlarge { + overflow-y: auto; +} +input.span1, textarea.span1 { + display: inline-block; + float: none; + width: 30px; + margin-left: 0; +} +input.span2, textarea.span2 { + display: inline-block; + float: none; + width: 90px; + margin-left: 0; +} +input.span3, textarea.span3 { + display: inline-block; + float: none; + width: 150px; + margin-left: 0; +} +input.span4, textarea.span4 { + display: inline-block; + float: none; + width: 210px; + margin-left: 0; +} +input.span5, textarea.span5 { + display: inline-block; + float: none; + width: 270px; + margin-left: 0; +} +input.span6, textarea.span6 { + display: inline-block; + float: none; + width: 330px; + margin-left: 0; +} +input.span7, textarea.span7 { + display: inline-block; + float: none; + width: 390px; + margin-left: 0; +} +input.span8, textarea.span8 { + display: inline-block; + float: none; + width: 450px; + margin-left: 0; +} +input.span9, textarea.span9 { + display: inline-block; + float: none; + width: 510px; + margin-left: 0; +} +input.span10, textarea.span10 { + display: inline-block; + float: none; + width: 570px; + margin-left: 0; +} +input.span11, textarea.span11 { + display: inline-block; + float: none; + width: 630px; + margin-left: 0; +} +input.span12, textarea.span12 { + display: inline-block; + float: none; + width: 690px; + margin-left: 0; +} +input.span13, textarea.span13 { + display: inline-block; + float: none; + width: 750px; + margin-left: 0; +} +input.span14, textarea.span14 { + display: inline-block; + float: none; + width: 810px; + margin-left: 0; +} +input.span15, textarea.span15 { + display: inline-block; + float: none; + width: 870px; + margin-left: 0; +} +input.span16, textarea.span16 { + display: inline-block; + float: none; + width: 930px; + margin-left: 0; +} +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + background-color: #f5f5f5; + border-color: #ddd; + cursor: not-allowed; +} +.actions { + background: #f5f5f5; + margin-top: 18px; + margin-bottom: 18px; + padding: 17px 20px 18px 150px; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.actions .secondary-action { + float: right; +} +.actions .secondary-action a { + line-height: 30px; +} +.actions .secondary-action a:hover { + text-decoration: underline; +} +.help-inline, .help-block { + font-size: 13px; + line-height: 18px; + color: #bfbfbf; +} +.help-inline { + padding-left: 5px; + *position: relative; + /* IE6-7 */ + + *top: -5px; + /* IE6-7 */ + +} +.help-block { + display: block; + max-width: 600px; +} +.inline-inputs { + color: #808080; +} +.inline-inputs span { + padding: 0 2px 0 1px; +} +.input-prepend input, .input-append input { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-prepend .add-on, .input-append .add-on { + position: relative; + background: #f5f5f5; + border: 1px solid #ccc; + z-index: 2; + float: left; + display: block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 4px 4px 5px; + margin-right: -1px; + font-weight: normal; + line-height: 18px; + color: #bfbfbf; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-prepend .active, .input-append .active { + background: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on { + *margin-top: 1px; + /* IE6-7 */ + +} +.input-append input { + float: left; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-append .add-on { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; + margin-right: 0; + margin-left: -1px; +} +.inputs-list { + margin: 0 0 5px; + width: 100%; +} +.inputs-list li { + display: block; + padding: 0; + width: 100%; +} +.inputs-list label { + display: block; + float: none; + width: auto; + padding: 0; + margin-left: 20px; + line-height: 18px; + text-align: left; + white-space: normal; +} +.inputs-list label strong { + color: #808080; +} +.inputs-list label small { + font-size: 11px; + font-weight: normal; +} +.inputs-list .inputs-list { + margin-left: 25px; + margin-bottom: 10px; + padding-top: 0; +} +.inputs-list:first-child { + padding-top: 6px; +} +.inputs-list li + li { + padding-top: 2px; +} +.inputs-list input[type=radio], .inputs-list input[type=checkbox] { + margin-bottom: 0; + margin-left: -20px; + float: left; +} +.form-stacked { + padding-left: 20px; +} +.form-stacked fieldset { + padding-top: 9px; +} +.form-stacked legend { + padding-left: 0; +} +.form-stacked label { + display: block; + float: none; + width: auto; + font-weight: bold; + text-align: left; + line-height: 20px; + padding-top: 0; +} +.form-stacked .clearfix { + margin-bottom: 9px; +} +.form-stacked .clearfix div.input { + margin-left: 0; +} +.form-stacked .inputs-list { + margin-bottom: 0; +} +.form-stacked .inputs-list li { + padding-top: 0; +} +.form-stacked .inputs-list li label { + font-weight: normal; + padding-top: 0; +} +.form-stacked div.clearfix.error { + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + margin-top: 0; + margin-left: -10px; +} +.form-stacked .actions { + margin-left: -20px; + padding-left: 20px; +} +/* + * Tables.less + * Tables for, you guessed it, tabular data + * ---------------------------------------- */ +table { + width: 100%; + margin-bottom: 18px; + padding: 0; + font-size: 13px; + border-collapse: collapse; +} +table th, table td { + padding: 10px 10px 9px; + line-height: 18px; + text-align: left; +} +table th { + padding-top: 9px; + font-weight: bold; + vertical-align: middle; +} +table td { + vertical-align: top; + border-top: 1px solid #ddd; +} +table tbody th { + border-top: 1px solid #ddd; + vertical-align: top; +} +.condensed-table th, .condensed-table td { + padding: 5px 5px 4px; +} +.bordered-table { + border: 1px solid #ddd; + border-collapse: separate; + *border-collapse: collapse; + /* IE7, collapse table to remove spacing */ + + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.bordered-table th + th, .bordered-table td + td, .bordered-table th + td { + border-left: 1px solid #ddd; +} +.bordered-table thead tr:first-child th:first-child, .bordered-table tbody tr:first-child td:first-child { + -webkit-border-radius: 4px 0 0 0; + -moz-border-radius: 4px 0 0 0; + border-radius: 4px 0 0 0; +} +.bordered-table thead tr:first-child th:last-child, .bordered-table tbody tr:first-child td:last-child { + -webkit-border-radius: 0 4px 0 0; + -moz-border-radius: 0 4px 0 0; + border-radius: 0 4px 0 0; +} +.bordered-table tbody tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; +} +.bordered-table tbody tr:last-child td:last-child { + -webkit-border-radius: 0 0 4px 0; + -moz-border-radius: 0 0 4px 0; + border-radius: 0 0 4px 0; +} +table .span1 { + width: 20px; +} +table .span2 { + width: 60px; +} +table .span3 { + width: 100px; +} +table .span4 { + width: 140px; +} +table .span5 { + width: 180px; +} +table .span6 { + width: 220px; +} +table .span7 { + width: 260px; +} +table .span8 { + width: 300px; +} +table .span9 { + width: 340px; +} +table .span10 { + width: 380px; +} +table .span11 { + width: 420px; +} +table .span12 { + width: 460px; +} +table .span13 { + width: 500px; +} +table .span14 { + width: 540px; +} +table .span15 { + width: 580px; +} +table .span16 { + width: 620px; +} +.zebra-striped tbody tr:nth-child(odd) td, .zebra-striped tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} +.zebra-striped tbody tr:hover td, .zebra-striped tbody tr:hover th { + background-color: #f5f5f5; +} +table .header { + cursor: pointer; +} +table .header:after { + content: ""; + float: right; + margin-top: 7px; + border-width: 0 4px 4px; + border-style: solid; + border-color: #000 transparent; + visibility: hidden; +} +table .headerSortUp, table .headerSortDown { + background-color: rgba(141, 192, 219, 0.25); + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); +} +table .header:hover:after { + visibility: visible; +} +table .headerSortDown:after, table .headerSortDown:hover:after { + visibility: visible; + filter: alpha(opacity=60); + -khtml-opacity: 0.6; + -moz-opacity: 0.6; + opacity: 0.6; +} +table .headerSortUp:after { + border-bottom: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #000; + visibility: visible; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + filter: alpha(opacity=60); + -khtml-opacity: 0.6; + -moz-opacity: 0.6; + opacity: 0.6; +} +table .blue { + color: #049cdb; + border-bottom-color: #049cdb; +} +table .headerSortUp.blue, table .headerSortDown.blue { + background-color: #ade6fe; +} +table .green { + color: #46a546; + border-bottom-color: #46a546; +} +table .headerSortUp.green, table .headerSortDown.green { + background-color: #cdeacd; +} +table .red { + color: #9d261d; + border-bottom-color: #9d261d; +} +table .headerSortUp.red, table .headerSortDown.red { + background-color: #f4c8c5; +} +table .yellow { + color: #ffc40d; + border-bottom-color: #ffc40d; +} +table .headerSortUp.yellow, table .headerSortDown.yellow { + background-color: #fff6d9; +} +table .orange { + color: #f89406; + border-bottom-color: #f89406; +} +table .headerSortUp.orange, table .headerSortDown.orange { + background-color: #fee9cc; +} +table .purple { + color: #7a43b6; + border-bottom-color: #7a43b6; +} +table .headerSortUp.purple, table .headerSortDown.purple { + background-color: #e2d5f0; +} +/* Patterns.less + * Repeatable UI elements outside the base styles provided from the scaffolding + * ---------------------------------------------------------------------------- */ +.topbar { + height: 40px; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 10000; + overflow: visible; +} +.topbar a { + color: #bfbfbf; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.topbar h3 a:hover, .topbar .brand:hover, .topbar ul .active > a { + background-color: #333; + background-color: rgba(255, 255, 255, 0.05); + color: #ffffff; + text-decoration: none; +} +.topbar h3 { + position: relative; +} +.topbar h3 a, .topbar .brand { + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; + color: #ffffff; + font-size: 20px; + font-weight: 200; + line-height: 1; +} +.topbar p { + margin: 0; + line-height: 40px; +} +.topbar p a:hover { + background-color: transparent; + color: #ffffff; +} +.topbar form { + float: left; + margin: 5px 0 0 0; + position: relative; + filter: alpha(opacity=100); + -khtml-opacity: 1; + -moz-opacity: 1; + opacity: 1; +} +.topbar form.pull-right { + float: right; +} +.topbar input { + background-color: #444; + background-color: rgba(255, 255, 255, 0.3); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: normal; + font-weight: 13px; + line-height: 1; + padding: 4px 9px; + color: #ffffff; + color: rgba(255, 255, 255, 0.75); + border: 1px solid #111; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + -webkit-transition: none; + -moz-transition: none; + -ms-transition: none; + -o-transition: none; + transition: none; +} +.topbar input:-moz-placeholder { + color: #e6e6e6; +} +.topbar input::-webkit-input-placeholder { + color: #e6e6e6; +} +.topbar input:hover { + background-color: #bfbfbf; + background-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} +.topbar input:focus, .topbar input.focused { + outline: 0; + background-color: #ffffff; + color: #404040; + text-shadow: 0 1px 0 #ffffff; + border: 0; + padding: 5px 10px; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} +.topbar-inner, .topbar .fill { + background-color: #222; + background-color: #222222; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} +.topbar div > ul, .nav { + display: block; + float: left; + margin: 0 10px 0 0; + position: relative; + left: 0; +} +.topbar div > ul > li, .nav > li { + display: block; + float: left; +} +.topbar div > ul a, .nav a { + display: block; + float: none; + padding: 10px 10px 11px; + line-height: 19px; + text-decoration: none; +} +.topbar div > ul a:hover, .nav a:hover { + color: #ffffff; + text-decoration: none; +} +.topbar div > ul .active > a, .nav .active > a { + background-color: #222; + background-color: rgba(0, 0, 0, 0.5); +} +.topbar div > ul.secondary-nav, .nav.secondary-nav { + float: right; + margin-left: 10px; + margin-right: 0; +} +.topbar div > ul.secondary-nav .menu-dropdown, +.nav.secondary-nav .menu-dropdown, +.topbar div > ul.secondary-nav .dropdown-menu, +.nav.secondary-nav .dropdown-menu { + right: 0; + border: 0; +} +.topbar div > ul a.menu:hover, +.nav a.menu:hover, +.topbar div > ul li.open .menu, +.nav li.open .menu, +.topbar div > ul .dropdown-toggle:hover, +.nav .dropdown-toggle:hover, +.topbar div > ul .dropdown.open .dropdown-toggle, +.nav .dropdown.open .dropdown-toggle { + background: #444; + background: rgba(255, 255, 255, 0.05); +} +.topbar div > ul .menu-dropdown, +.nav .menu-dropdown, +.topbar div > ul .dropdown-menu, +.nav .dropdown-menu { + background-color: #333; +} +.topbar div > ul .menu-dropdown a.menu, +.nav .menu-dropdown a.menu, +.topbar div > ul .dropdown-menu a.menu, +.nav .dropdown-menu a.menu, +.topbar div > ul .menu-dropdown .dropdown-toggle, +.nav .menu-dropdown .dropdown-toggle, +.topbar div > ul .dropdown-menu .dropdown-toggle, +.nav .dropdown-menu .dropdown-toggle { + color: #ffffff; +} +.topbar div > ul .menu-dropdown a.menu.open, +.nav .menu-dropdown a.menu.open, +.topbar div > ul .dropdown-menu a.menu.open, +.nav .dropdown-menu a.menu.open, +.topbar div > ul .menu-dropdown .dropdown-toggle.open, +.nav .menu-dropdown .dropdown-toggle.open, +.topbar div > ul .dropdown-menu .dropdown-toggle.open, +.nav .dropdown-menu .dropdown-toggle.open { + background: #444; + background: rgba(255, 255, 255, 0.05); +} +.topbar div > ul .menu-dropdown li a, +.nav .menu-dropdown li a, +.topbar div > ul .dropdown-menu li a, +.nav .dropdown-menu li a { + color: #999; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); +} +.topbar div > ul .menu-dropdown li a:hover, +.nav .menu-dropdown li a:hover, +.topbar div > ul .dropdown-menu li a:hover, +.nav .dropdown-menu li a:hover { + background-color: #191919; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919)); + background-image: -moz-linear-gradient(top, #292929, #191919); + background-image: -ms-linear-gradient(top, #292929, #191919); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919)); + background-image: -webkit-linear-gradient(top, #292929, #191919); + background-image: -o-linear-gradient(top, #292929, #191919); + background-image: linear-gradient(top, #292929, #191919); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0); + color: #ffffff; +} +.topbar div > ul .menu-dropdown .active a, +.nav .menu-dropdown .active a, +.topbar div > ul .dropdown-menu .active a, +.nav .dropdown-menu .active a { + color: #ffffff; +} +.topbar div > ul .menu-dropdown .divider, +.nav .menu-dropdown .divider, +.topbar div > ul .dropdown-menu .divider, +.nav .dropdown-menu .divider { + background-color: #222; + border-color: #444; +} +.topbar ul .menu-dropdown li a, .topbar ul .dropdown-menu li a { + padding: 4px 15px; +} +li.menu, .dropdown { + position: relative; +} +a.menu:after, .dropdown-toggle:after { + width: 0; + height: 0; + display: inline-block; + content: "↓"; + text-indent: -99999px; + vertical-align: top; + margin-top: 8px; + margin-left: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #ffffff; + filter: alpha(opacity=50); + -khtml-opacity: 0.5; + -moz-opacity: 0.5; + opacity: 0.5; +} +.menu-dropdown, .dropdown-menu { + background-color: #ffffff; + float: left; + display: none; + position: absolute; + top: 40px; + z-index: 900; + min-width: 160px; + max-width: 220px; + _width: 160px; + margin-left: 0; + margin-right: 0; + padding: 6px 0; + zoom: 1; + border-color: #999; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 0 1px 1px; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.menu-dropdown li, .dropdown-menu li { + float: none; + display: block; + background-color: none; +} +.menu-dropdown .divider, .dropdown-menu .divider { + height: 1px; + margin: 5px 0; + overflow: hidden; + background-color: #eee; + border-bottom: 1px solid #ffffff; +} +.topbar .dropdown-menu a, .dropdown-menu a { + display: block; + padding: 4px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #808080; + text-shadow: 0 1px 0 #ffffff; +} +.topbar .dropdown-menu a:hover, +.dropdown-menu a:hover, +.topbar .dropdown-menu a.hover, +.dropdown-menu a.hover { + background-color: #dddddd; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd)); + background-image: -moz-linear-gradient(top, #eeeeee, #dddddd); + background-image: -ms-linear-gradient(top, #eeeeee, #dddddd); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd)); + background-image: -webkit-linear-gradient(top, #eeeeee, #dddddd); + background-image: -o-linear-gradient(top, #eeeeee, #dddddd); + background-image: linear-gradient(top, #eeeeee, #dddddd); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0); + color: #404040; + text-decoration: none; + -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); +} +.open .menu, +.dropdown.open .menu, +.open .dropdown-toggle, +.dropdown.open .dropdown-toggle { + color: #ffffff; + background: #ccc; + background: rgba(0, 0, 0, 0.3); +} +.open .menu-dropdown, +.dropdown.open .menu-dropdown, +.open .dropdown-menu, +.dropdown.open .dropdown-menu { + display: block; +} +.tabs, .pills { + margin: 0 0 18px; + padding: 0; + list-style: none; + zoom: 1; +} +.tabs:before, +.pills:before, +.tabs:after, +.pills:after { + display: table; + content: ""; + zoom: 1; +} +.tabs:after, .pills:after { + clear: both; +} +.tabs > li, .pills > li { + float: left; +} +.tabs > li > a, .pills > li > a { + display: block; +} +.tabs { + border-color: #ddd; + border-style: solid; + border-width: 0 0 1px; +} +.tabs > li { + position: relative; + margin-bottom: -1px; +} +.tabs > li > a { + padding: 0 15px; + margin-right: 2px; + line-height: 34px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.tabs > li > a:hover { + text-decoration: none; + background-color: #eee; + border-color: #eee #eee #ddd; +} +.tabs .active > a, .tabs .active > a:hover { + color: #808080; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.tabs .menu-dropdown, .tabs .dropdown-menu { + top: 35px; + border-width: 1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} +.tabs a.menu:after, .tabs .dropdown-toggle:after { + border-top-color: #999; + margin-top: 15px; + margin-left: 5px; +} +.tabs li.open.menu .menu, .tabs .open.dropdown .dropdown-toggle { + border-color: #999; +} +.tabs li.open a.menu:after, .tabs .dropdown.open .dropdown-toggle:after { + border-top-color: #555; +} +.pills a { + margin: 5px 3px 5px 0; + padding: 0 15px; + line-height: 30px; + text-shadow: 0 1px 1px #ffffff; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pills a:hover { + color: #ffffff; + text-decoration: none; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + background-color: #00438a; +} +.pills .active a { + color: #ffffff; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + background-color: #0069d6; +} +.pills-vertical > li { + float: none; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane, +.tab-content > div, +.pill-content > div { + display: none; +} +.tab-content > .active, .pill-content > .active { + display: block; +} +.breadcrumb { + padding: 7px 14px; + margin: 0 0 18px; + background-color: #f5f5f5; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5)); + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} +.breadcrumb li { + display: inline; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb .divider { + padding: 0 5px; + color: #bfbfbf; +} +.breadcrumb .active a { + color: #404040; +} +.hero-unit { + background-color: #f5f5f5; + margin-bottom: 30px; + padding: 60px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; +} +.hero-unit p { + font-size: 18px; + font-weight: 200; + line-height: 27px; +} +footer { + margin-top: 17px; + padding-top: 17px; + border-top: 1px solid #eee; +} +.page-header { + margin-bottom: 17px; + border-bottom: 1px solid #ddd; + -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.page-header h1 { + margin-bottom: 8px; +} +.btn.danger, +.alert-message.danger, +.btn.danger:hover, +.alert-message.danger:hover, +.btn.error, +.alert-message.error, +.btn.error:hover, +.alert-message.error:hover, +.btn.success, +.alert-message.success, +.btn.success:hover, +.alert-message.success:hover, +.btn.info, +.alert-message.info, +.btn.info:hover, +.alert-message.info:hover { + color: #ffffff; +} +.btn .close, .alert-message .close { + font-family: Arial, sans-serif; + line-height: 18px; +} +.btn.danger, +.alert-message.danger, +.btn.error, +.alert-message.error { + background-color: #c43c35; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #c43c35 #c43c35 #882a25; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} +.btn.success, .alert-message.success { + background-color: #57a957; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957)); + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -ms-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(top, #62c462, #57a957); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #57a957 #57a957 #3d773d; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} +.btn.info, .alert-message.info { + background-color: #339bb9; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9)); + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(top, #5bc0de, #339bb9); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #339bb9 #339bb9 #22697d; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} +.btn { + cursor: pointer; + display: inline-block; + background-color: #e6e6e6; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); + background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + padding: 5px 14px 6px; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + color: #333; + font-size: 13px; + line-height: normal; + border: 1px solid #ccc; + border-bottom-color: #bbb; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -webkit-transition: 0.1s linear all; + -moz-transition: 0.1s linear all; + -ms-transition: 0.1s linear all; + -o-transition: 0.1s linear all; + transition: 0.1s linear all; +} +.btn:hover { + background-position: 0 -15px; + color: #333; + text-decoration: none; +} +.btn:focus { + outline: 1px dotted #666; +} +.btn.primary { + color: #ffffff; + 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); +} +.btn.active, .btn:active { + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); +} +.btn.disabled { + cursor: default; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + filter: alpha(opacity=65); + -khtml-opacity: 0.65; + -moz-opacity: 0.65; + opacity: 0.65; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn[disabled] { + cursor: default; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + filter: alpha(opacity=65); + -khtml-opacity: 0.65; + -moz-opacity: 0.65; + opacity: 0.65; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn.large { + font-size: 15px; + line-height: normal; + padding: 9px 14px 9px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.btn.small { + padding: 7px 9px 7px; + font-size: 11px; +} +:root .alert-message, :root .btn { + border-radius: 0 \0; +} +button.btn::-moz-focus-inner, input[type=submit].btn::-moz-focus-inner { + padding: 0; + border: 0; +} +.close { + float: right; + color: #000000; + font-size: 20px; + font-weight: bold; + line-height: 13.5px; + text-shadow: 0 1px 0 #ffffff; + filter: alpha(opacity=25); + -khtml-opacity: 0.25; + -moz-opacity: 0.25; + opacity: 0.25; +} +.close:hover { + color: #000000; + text-decoration: none; + filter: alpha(opacity=40); + -khtml-opacity: 0.4; + -moz-opacity: 0.4; + opacity: 0.4; +} +.alert-message { + position: relative; + padding: 7px 15px; + margin-bottom: 18px; + color: #404040; + background-color: #eedc94; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94)); + background-image: -moz-linear-gradient(top, #fceec1, #eedc94); + background-image: -ms-linear-gradient(top, #fceec1, #eedc94); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94)); + background-image: -webkit-linear-gradient(top, #fceec1, #eedc94); + background-image: -o-linear-gradient(top, #fceec1, #eedc94); + background-image: linear-gradient(top, #fceec1, #eedc94); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #eedc94 #eedc94 #e4c652; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + border-width: 1px; + border-style: solid; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); +} +.alert-message .close { + margin-top: 1px; + *margin-top: 0; +} +.alert-message a { + font-weight: bold; + color: #404040; +} +.alert-message.danger p a, +.alert-message.error p a, +.alert-message.success p a, +.alert-message.info p a { + color: #ffffff; +} +.alert-message h5 { + line-height: 18px; +} +.alert-message p { + margin-bottom: 0; +} +.alert-message div { + margin-top: 5px; + margin-bottom: 2px; + line-height: 28px; +} +.alert-message .btn { + -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); +} +.alert-message.block-message { + background-image: none; + background-color: #fdf5d9; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + padding: 14px; + border-color: #fceec1; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.alert-message.block-message ul, .alert-message.block-message p { + margin-right: 30px; +} +.alert-message.block-message ul { + margin-bottom: 0; +} +.alert-message.block-message li { + color: #404040; +} +.alert-message.block-message .alert-actions { + margin-top: 5px; +} +.alert-message.block-message.error, .alert-message.block-message.success, .alert-message.block-message.info { + color: #404040; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.alert-message.block-message.error { + background-color: #fddfde; + border-color: #fbc7c6; +} +.alert-message.block-message.success { + background-color: #d1eed1; + border-color: #bfe7bf; +} +.alert-message.block-message.info { + background-color: #ddf4fb; + border-color: #c6edf9; +} +.alert-message.block-message.danger p a, +.alert-message.block-message.error p a, +.alert-message.block-message.success p a, +.alert-message.block-message.info p a { + color: #404040; +} +.pagination { + height: 36px; + margin: 18px 0; +} +.pagination ul { + float: left; + margin: 0; + border: 1px solid #ddd; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination li { + display: inline; +} +.pagination a { + float: left; + padding: 0 14px; + line-height: 34px; + border-right: 1px solid; + border-right-color: #ddd; + border-right-color: rgba(0, 0, 0, 0.15); + *border-right-color: #ddd; + /* IE6-7 */ + + text-decoration: none; +} +.pagination a:hover, .pagination .active a { + background-color: #c7eefe; +} +.pagination .disabled a, .pagination .disabled a:hover { + background-color: transparent; + color: #bfbfbf; +} +.pagination .next a { + border: 0; +} +.well { + background-color: #f5f5f5; + margin-bottom: 20px; + padding: 19px; + min-height: 20px; + border: 1px solid #eee; + border: 1px solid rgba(0, 0, 0, 0.05); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.modal-backdrop { + background-color: #000000; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10000; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, .modal-backdrop.fade.in { + filter: alpha(opacity=80); + -khtml-opacity: 0.8; + -moz-opacity: 0.8; + opacity: 0.8; +} +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 11000; + max-height: 500px; + overflow: auto; + width: 560px; + margin: -250px 0 0 -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + /* IE6-7 */ + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.modal .close { + margin-top: 7px; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -ms-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 50%; +} +.modal-header { + border-bottom: 1px solid #eee; + padding: 5px 15px; +} +.modal-body { + padding: 15px; +} +.modal-body form { + margin-bottom: 0; +} +.modal-footer { + background-color: #f5f5f5; + padding: 14px 15px 15px; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + zoom: 1; + margin-bottom: 0; +} +.modal-footer:before, .modal-footer:after { + display: table; + content: ""; + zoom: 1; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn { + float: right; + margin-left: 5px; +} +.modal .popover, .modal .twipsy { + z-index: 12000; +} +.twipsy { + display: block; + position: absolute; + visibility: visible; + padding: 5px; + font-size: 11px; + z-index: 1000; + filter: alpha(opacity=80); + -khtml-opacity: 0.8; + -moz-opacity: 0.8; + opacity: 0.8; +} +.twipsy.fade.in { + filter: alpha(opacity=80); + -khtml-opacity: 0.8; + -moz-opacity: 0.8; + opacity: 0.8; +} +.twipsy.above .twipsy-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.twipsy.left .twipsy-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.twipsy.below .twipsy-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.twipsy.right .twipsy-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.twipsy-inner { + padding: 3px 8px; + background-color: #000000; + color: white; + text-align: center; + max-width: 200px; + text-decoration: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.twipsy-arrow { + position: absolute; + width: 0; + height: 0; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1000; + padding: 5px; + display: none; +} +.popover.above .arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.popover.right .arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.popover.below .arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.popover.left .arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.popover .arrow { + position: absolute; + width: 0; + height: 0; +} +.popover .inner { + background: #000000; + background: rgba(0, 0, 0, 0.8); + padding: 3px; + overflow: hidden; + width: 280px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); +} +.popover .title { + background-color: #f5f5f5; + padding: 9px 15px; + line-height: 1; + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #eee; +} +.popover .content { + background-color: #ffffff; + padding: 14px; + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.popover .content p, .popover .content ul, .popover .content ol { + margin-bottom: 0; +} +.fade { + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -ms-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; + opacity: 0; +} +.fade.in { + opacity: 1; +} +.label { + padding: 1px 3px 2px; + font-size: 9.75px; + font-weight: bold; + color: #ffffff; + text-transform: uppercase; + white-space: nowrap; + background-color: #bfbfbf; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + text-shadow: none; +} +.label.important { + background-color: #c43c35; +} +.label.warning { + background-color: #f89406; +} +.label.success { + background-color: #46a546; +} +.label.notice { + background-color: #62cffc; +} +.media-grid { + margin-left: -20px; + margin-bottom: 0; + zoom: 1; +} +.media-grid:before, .media-grid:after { + display: table; + content: ""; + zoom: 1; +} +.media-grid:after { + clear: both; +} +.media-grid li { + display: inline; +} +.media-grid a { + float: left; + padding: 4px; + margin: 0 0 18px 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); +} +.media-grid a img { + display: block; +} +.media-grid a:hover { + border-color: #0069d6; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-alerts.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-alerts.js new file mode 100644 index 00000000000..3ef1672bc0c --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-alerts.js @@ -0,0 +1,124 @@ +/* ========================================================== + * bootstrap-alerts.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#alerts + * ========================================================== + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function( $ ){ + + "use strict" + + /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) + * ======================================================= */ + + var transitionEnd + + $(document).ready(function () { + + $.support.transition = (function () { + var thisBody = document.body || document.documentElement + , thisStyle = thisBody.style + , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined + return support + })() + + // set CSS transition event type + if ( $.support.transition ) { + transitionEnd = "TransitionEnd" + if ( $.browser.webkit ) { + transitionEnd = "webkitTransitionEnd" + } else if ( $.browser.mozilla ) { + transitionEnd = "transitionend" + } else if ( $.browser.opera ) { + transitionEnd = "oTransitionEnd" + } + } + + }) + + /* ALERT CLASS DEFINITION + * ====================== */ + + var Alert = function ( content, options ) { + if (options == 'close') return this.close.call(content) + this.settings = $.extend({}, $.fn.alert.defaults, options) + this.$element = $(content) + .delegate(this.settings.selector, 'click', this.close) + } + + Alert.prototype = { + + close: function (e) { + var $element = $(this) + , className = 'alert-message' + + $element = $element.hasClass(className) ? $element : $element.parent() + + e && e.preventDefault() + $element.removeClass('in') + + function removeElement () { + $element.remove() + } + + $.support.transition && $element.hasClass('fade') ? + $element.bind(transitionEnd, removeElement) : + removeElement() + } + + } + + + /* ALERT PLUGIN DEFINITION + * ======================= */ + + $.fn.alert = function ( options ) { + + if ( options === true ) { + return this.data('alert') + } + + return this.each(function () { + var $this = $(this) + , data + + if ( typeof options == 'string' ) { + + data = $this.data('alert') + + if (typeof data == 'object') { + return data[options].call( $this ) + } + + } + + $(this).data('alert', new Alert( this, options )) + + }) + } + + $.fn.alert.defaults = { + selector: '.close' + } + + $(document).ready(function () { + new Alert($('body'), { + selector: '.alert-message[data-alert] .close' + }) + }) + +}( window.jQuery || window.ender );
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-buttons.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-buttons.js new file mode 100644 index 00000000000..ba52f1c06b8 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-buttons.js @@ -0,0 +1,64 @@ +/* ============================================================ + * bootstrap-buttons.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#buttons + * ============================================================ + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + function setState(el, state) { + var d = 'disabled' + , $el = $(el) + , data = $el.data() + + state = state + 'Text' + data.resetText || $el.data('resetText', $el.html()) + + $el.html( data[state] || $.fn.button.defaults[state] ) + + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } + + function toggle(el) { + $(el).toggleClass('active') + } + + $.fn.button = function(options) { + return this.each(function () { + if (options == 'toggle') { + return toggle(this) + } + options && setState(this, options) + }) + } + + $.fn.button.defaults = { + loadingText: 'loading...' + } + + $(function () { + $('body').delegate('.btn[data-toggle]', 'click', function () { + $(this).button('toggle') + }) + }) + +}( window.jQuery || window.ender );
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-dropdown.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-dropdown.js new file mode 100644 index 00000000000..fda6da597e0 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-dropdown.js @@ -0,0 +1,55 @@ +/* ============================================================ + * bootstrap-dropdown.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#dropdown + * ============================================================ + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function( $ ){ + + "use strict" + + /* DROPDOWN PLUGIN DEFINITION + * ========================== */ + + $.fn.dropdown = function ( selector ) { + return this.each(function () { + $(this).delegate(selector || d, 'click', function (e) { + var li = $(this).parent('li') + , isActive = li.hasClass('open') + + clearMenus() + !isActive && li.toggleClass('open') + return false + }) + }) + } + + /* APPLY TO STANDARD DROPDOWN ELEMENTS + * =================================== */ + + var d = 'a.menu, .dropdown-toggle' + + function clearMenus() { + $(d).parent('li').removeClass('open') + } + + $(function () { + $('html').bind("click", clearMenus) + $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' ) + }) + +}( window.jQuery || window.ender ); diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-modal.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-modal.js new file mode 100644 index 00000000000..b328217f813 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-modal.js @@ -0,0 +1,260 @@ +/* ========================================================= + * bootstrap-modal.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#modal + * ========================================================= + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= */ + + +!function( $ ){ + + "use strict" + + /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) + * ======================================================= */ + + var transitionEnd + + $(document).ready(function () { + + $.support.transition = (function () { + var thisBody = document.body || document.documentElement + , thisStyle = thisBody.style + , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined + return support + })() + + // set CSS transition event type + if ( $.support.transition ) { + transitionEnd = "TransitionEnd" + if ( $.browser.webkit ) { + transitionEnd = "webkitTransitionEnd" + } else if ( $.browser.mozilla ) { + transitionEnd = "transitionend" + } else if ( $.browser.opera ) { + transitionEnd = "oTransitionEnd" + } + } + + }) + + + /* MODAL PUBLIC CLASS DEFINITION + * ============================= */ + + var Modal = function ( content, options ) { + this.settings = $.extend({}, $.fn.modal.defaults, options) + this.$element = $(content) + .delegate('.close', 'click.modal', $.proxy(this.hide, this)) + + if ( this.settings.show ) { + this.show() + } + + return this + } + + Modal.prototype = { + + toggle: function () { + return this[!this.isShown ? 'show' : 'hide']() + } + + , show: function () { + var that = this + this.isShown = true + this.$element.trigger('show') + + escape.call(this) + backdrop.call(this, function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + that.$element + .appendTo(document.body) + .show() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + transition ? + that.$element.one(transitionEnd, function () { that.$element.trigger('shown') }) : + that.$element.trigger('shown') + + }) + + return this + } + + , hide: function (e) { + e && e.preventDefault() + + if ( !this.isShown ) { + return this + } + + var that = this + this.isShown = false + + escape.call(this) + + this.$element + .trigger('hide') + .removeClass('in') + + $.support.transition && this.$element.hasClass('fade') ? + hideWithTransition.call(this) : + hideModal.call(this) + + return this + } + + } + + + /* MODAL PRIVATE METHODS + * ===================== */ + + function hideWithTransition() { + // firefox drops transitionEnd events :{o + var that = this + , timeout = setTimeout(function () { + that.$element.unbind(transitionEnd) + hideModal.call(that) + }, 500) + + this.$element.one(transitionEnd, function () { + clearTimeout(timeout) + hideModal.call(that) + }) + } + + function hideModal (that) { + this.$element + .hide() + .trigger('hidden') + + backdrop.call(this) + } + + function backdrop ( callback ) { + var that = this + , animate = this.$element.hasClass('fade') ? 'fade' : '' + if ( this.isShown && this.settings.backdrop ) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') + .appendTo(document.body) + + if ( this.settings.backdrop != 'static' ) { + this.$backdrop.click($.proxy(this.hide, this)) + } + + if ( doAnimate ) { + this.$backdrop[0].offsetWidth // force reflow + } + + this.$backdrop.addClass('in') + + doAnimate ? + this.$backdrop.one(transitionEnd, callback) : + callback() + + } else if ( !this.isShown && this.$backdrop ) { + this.$backdrop.removeClass('in') + + $.support.transition && this.$element.hasClass('fade')? + this.$backdrop.one(transitionEnd, $.proxy(removeBackdrop, this)) : + removeBackdrop.call(this) + + } else if ( callback ) { + callback() + } + } + + function removeBackdrop() { + this.$backdrop.remove() + this.$backdrop = null + } + + function escape() { + var that = this + if ( this.isShown && this.settings.keyboard ) { + $(document).bind('keyup.modal', function ( e ) { + if ( e.which == 27 ) { + that.hide() + } + }) + } else if ( !this.isShown ) { + $(document).unbind('keyup.modal') + } + } + + + /* MODAL PLUGIN DEFINITION + * ======================= */ + + $.fn.modal = function ( options ) { + var modal = this.data('modal') + + if (!modal) { + + if (typeof options == 'string') { + options = { + show: /show|toggle/.test(options) + } + } + + return this.each(function () { + $(this).data('modal', new Modal(this, options)) + }) + } + + if ( options === true ) { + return modal + } + + if ( typeof options == 'string' ) { + modal[options]() + } else if ( modal ) { + modal.toggle() + } + + return this + } + + $.fn.modal.Modal = Modal + + $.fn.modal.defaults = { + backdrop: false + , keyboard: false + , show: false + } + + + /* MODAL DATA- IMPLEMENTATION + * ========================== */ + + $(document).ready(function () { + $('body').delegate('[data-controls-modal]', 'click', function (e) { + e.preventDefault() + var $this = $(this).data('show', true) + $('#' + $this.attr('data-controls-modal')).modal( $this.data() ) + }) + }) + +}( window.jQuery || window.ender ); diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-popover.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-popover.js new file mode 100644 index 00000000000..39fb575bdb5 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-popover.js @@ -0,0 +1,90 @@ +/* =========================================================== + * bootstrap-popover.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#popover + * =========================================================== + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================================================== */ + + +!function( $ ) { + + "use strict" + + var Popover = function ( element, options ) { + this.$element = $(element) + this.options = options + this.enabled = true + this.fixTitle() + } + + /* NOTE: POPOVER EXTENDS BOOTSTRAP-TWIPSY.js + ========================================= */ + + Popover.prototype = $.extend({}, $.fn.twipsy.Twipsy.prototype, { + + setContent: function () { + var $tip = this.tip() + $tip.find('.title')[this.options.html ? 'html' : 'text'](this.getTitle()) + $tip.find('.content > *')[this.options.html ? 'html' : 'text'](this.getContent()) + $tip[0].className = 'popover' + } + + , hasContent: function () { + return this.getTitle() || this.getContent() + } + + , getContent: function () { + var content + , $e = this.$element + , o = this.options + + if (typeof this.options.content == 'string') { + content = $e.attr(this.options.content) + } else if (typeof this.options.content == 'function') { + content = this.options.content.call(this.$element[0]) + } + + return content + } + + , tip: function() { + if (!this.$tip) { + this.$tip = $('<div class="popover" />') + .html(this.options.template) + } + return this.$tip + } + + }) + + + /* POPOVER PLUGIN DEFINITION + * ======================= */ + + $.fn.popover = function (options) { + if (typeof options == 'object') options = $.extend({}, $.fn.popover.defaults, options) + $.fn.twipsy.initWith.call(this, options, Popover, 'popover') + return this + } + + $.fn.popover.defaults = $.extend({} , $.fn.twipsy.defaults, { + placement: 'right' + , content: 'data-content' + , template: '<div class="arrow"></div><div class="inner"><h3 class="title"></h3><div class="content"><p></p></div></div>' + }) + + $.fn.twipsy.rejectAttrOptions.push( 'content' ) + +}( window.jQuery || window.ender ); diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-scrollspy.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-scrollspy.js new file mode 100644 index 00000000000..efbc4329605 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-scrollspy.js @@ -0,0 +1,107 @@ +/* ============================================================= + * bootstrap-scrollspy.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#scrollspy + * ============================================================= + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================== */ + + +!function ( $ ) { + + "use strict" + + var $window = $(window) + + function ScrollSpy( topbar, selector ) { + var processScroll = $.proxy(this.processScroll, this) + this.$topbar = $(topbar) + this.selector = selector || 'li > a' + this.refresh() + this.$topbar.delegate(this.selector, 'click', processScroll) + $window.scroll(processScroll) + this.processScroll() + } + + ScrollSpy.prototype = { + + refresh: function () { + this.targets = this.$topbar.find(this.selector).map(function () { + var href = $(this).attr('href') + return /^#\w/.test(href) && $(href).length ? href : null + }) + + this.offsets = $.map(this.targets, function (id) { + return $(id).offset().top + }) + } + + , processScroll: function () { + var scrollTop = $window.scrollTop() + 10 + , offsets = this.offsets + , targets = this.targets + , activeTarget = this.activeTarget + , i + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) + && this.activateButton( targets[i] ) + } + } + + , activateButton: function (target) { + this.activeTarget = target + + this.$topbar + .find(this.selector).parent('.active') + .removeClass('active') + + this.$topbar + .find(this.selector + '[href="' + target + '"]') + .parent('li') + .addClass('active') + } + + } + + /* SCROLLSPY PLUGIN DEFINITION + * =========================== */ + + $.fn.scrollSpy = function( options ) { + var scrollspy = this.data('scrollspy') + + if (!scrollspy) { + return this.each(function () { + $(this).data('scrollspy', new ScrollSpy( this, options )) + }) + } + + if ( options === true ) { + return scrollspy + } + + if ( typeof options == 'string' ) { + scrollspy[options]() + } + + return this + } + + $(document).ready(function () { + $('body').scrollSpy('[data-scrollspy] li > a') + }) + +}( window.jQuery || window.ender );
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-tabs.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-tabs.js new file mode 100644 index 00000000000..a3c7ee14423 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-tabs.js @@ -0,0 +1,80 @@ +/* ======================================================== + * bootstrap-tabs.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#tabs + * ======================================================== + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================== */ + + +!function( $ ){ + + "use strict" + + function activate ( element, container ) { + container + .find('> .active') + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if ( element.parent('.dropdown-menu') ) { + element.closest('li.dropdown').addClass('active') + } + } + + function tab( e ) { + var $this = $(this) + , $ul = $this.closest('ul:not(.dropdown-menu)') + , href = $this.attr('href') + , previous + , $href + + if ( /^#\w+/.test(href) ) { + e.preventDefault() + + if ( $this.parent('li').hasClass('active') ) { + return + } + + previous = $ul.find('.active a').last()[0] + $href = $(href) + + activate($this.parent('li'), $ul) + activate($href, $href.parent()) + + $this.trigger({ + type: 'change' + , relatedTarget: previous + }) + } + } + + + /* TABS/PILLS PLUGIN DEFINITION + * ============================ */ + + $.fn.tabs = $.fn.pills = function ( selector ) { + return this.each(function () { + $(this).delegate(selector || '.tabs li > a, .pills > li > a', 'click', tab) + }) + } + + $(document).ready(function () { + $('body').tabs('ul[data-tabs] li > a, ul[data-pills] > li > a') + }) + +}( window.jQuery || window.ender ); diff --git a/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-twipsy.js b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-twipsy.js new file mode 100644 index 00000000000..5ebbddd675d --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/bootstrap/js/bootstrap-twipsy.js @@ -0,0 +1,321 @@ +/* ========================================================== + * bootstrap-twipsy.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#twipsy + * Adapted from the original jQuery.tipsy by Jason Frame + * ========================================================== + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function( $ ) { + + "use strict" + + /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) + * ======================================================= */ + + var transitionEnd + + $(document).ready(function () { + + $.support.transition = (function () { + var thisBody = document.body || document.documentElement + , thisStyle = thisBody.style + , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined + return support + })() + + // set CSS transition event type + if ( $.support.transition ) { + transitionEnd = "TransitionEnd" + if ( $.browser.webkit ) { + transitionEnd = "webkitTransitionEnd" + } else if ( $.browser.mozilla ) { + transitionEnd = "transitionend" + } else if ( $.browser.opera ) { + transitionEnd = "oTransitionEnd" + } + } + + }) + + + /* TWIPSY PUBLIC CLASS DEFINITION + * ============================== */ + + var Twipsy = function ( element, options ) { + this.$element = $(element) + this.options = options + this.enabled = true + this.fixTitle() + } + + Twipsy.prototype = { + + show: function() { + var pos + , actualWidth + , actualHeight + , placement + , $tip + , tp + + if (this.hasContent() && this.enabled) { + $tip = this.tip() + this.setContent() + + if (this.options.animate) { + $tip.addClass('fade') + } + + $tip + .remove() + .css({ top: 0, left: 0, display: 'block' }) + .prependTo(document.body) + + pos = $.extend({}, this.$element.offset(), { + width: this.$element[0].offsetWidth + , height: this.$element[0].offsetHeight + }) + + actualWidth = $tip[0].offsetWidth + actualHeight = $tip[0].offsetHeight + + placement = maybeCall(this.options.placement, this, [ $tip[0], this.$element[0] ]) + + switch (placement) { + case 'below': + tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'above': + tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'left': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset} + break + case 'right': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset} + break + } + + $tip + .css(tp) + .addClass(placement) + .addClass('in') + } + } + + , setContent: function () { + var $tip = this.tip() + $tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle()) + $tip[0].className = 'twipsy' + } + + , hide: function() { + var that = this + , $tip = this.tip() + + $tip.removeClass('in') + + function removeElement () { + $tip.remove() + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip.bind(transitionEnd, removeElement) : + removeElement() + } + + , fixTitle: function() { + var $e = this.$element + if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title') + } + } + + , hasContent: function () { + return this.getTitle() + } + + , getTitle: function() { + var title + , $e = this.$element + , o = this.options + + this.fixTitle() + + if (typeof o.title == 'string') { + title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title) + } else if (typeof o.title == 'function') { + title = o.title.call($e[0]) + } + + title = ('' + title).replace(/(^\s*|\s*$)/, "") + + return title || o.fallback + } + + , tip: function() { + return this.$tip = this.$tip || $('<div class="twipsy" />').html(this.options.template) + } + + , validate: function() { + if (!this.$element[0].parentNode) { + this.hide() + this.$element = null + this.options = null + } + } + + , enable: function() { + this.enabled = true + } + + , disable: function() { + this.enabled = false + } + + , toggleEnabled: function() { + this.enabled = !this.enabled + } + + , toggle: function () { + this[this.tip().hasClass('in') ? 'hide' : 'show']() + } + + } + + + /* TWIPSY PRIVATE METHODS + * ====================== */ + + function maybeCall ( thing, ctx, args ) { + return typeof thing == 'function' ? thing.apply(ctx, args) : thing + } + + /* TWIPSY PLUGIN DEFINITION + * ======================== */ + + $.fn.twipsy = function (options) { + $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy') + return this + } + + $.fn.twipsy.initWith = function (options, Constructor, name) { + var twipsy + , binder + , eventIn + , eventOut + + if (options === true) { + return this.data(name) + } else if (typeof options == 'string') { + twipsy = this.data(name) + if (twipsy) { + twipsy[options]() + } + return this + } + + options = $.extend({}, $.fn[name].defaults, options) + + function get(ele) { + var twipsy = $.data(ele, name) + + if (!twipsy) { + twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options)) + $.data(ele, name, twipsy) + } + + return twipsy + } + + function enter() { + var twipsy = get(this) + twipsy.hoverState = 'in' + + if (options.delayIn == 0) { + twipsy.show() + } else { + twipsy.fixTitle() + setTimeout(function() { + if (twipsy.hoverState == 'in') { + twipsy.show() + } + }, options.delayIn) + } + } + + function leave() { + var twipsy = get(this) + twipsy.hoverState = 'out' + if (options.delayOut == 0) { + twipsy.hide() + } else { + setTimeout(function() { + if (twipsy.hoverState == 'out') { + twipsy.hide() + } + }, options.delayOut) + } + } + + if (!options.live) { + this.each(function() { + get(this) + }) + } + + if (options.trigger != 'manual') { + binder = options.live ? 'live' : 'bind' + eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus' + eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur' + this[binder](eventIn, enter)[binder](eventOut, leave) + } + + return this + } + + $.fn.twipsy.Twipsy = Twipsy + + $.fn.twipsy.defaults = { + animate: true + , delayIn: 0 + , delayOut: 0 + , fallback: '' + , placement: 'above' + , html: false + , live: false + , offset: 0 + , title: 'title' + , trigger: 'hover' + , template: '<div class="twipsy-arrow"></div><div class="twipsy-inner"></div>' + } + + $.fn.twipsy.rejectAttrOptions = [ 'title' ] + + $.fn.twipsy.elementOptions = function(ele, options) { + var data = $(ele).data() + , rejects = $.fn.twipsy.rejectAttrOptions + , i = rejects.length + + while (i--) { + delete data[rejects[i]] + } + + return $.extend({}, options, data) + } + +}( window.jQuery || window.ender );
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png Binary files differnew file mode 100755 index 00000000000..5b5dab2ab7b --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png Binary files differnew file mode 100755 index 00000000000..ad3d6346e00 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png Binary files differnew file mode 100755 index 00000000000..42ccba269b6 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png Binary files differnew file mode 100755 index 00000000000..5a46b47cb16 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png Binary files differnew file mode 100755 index 00000000000..86c2baa655e --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png Binary files differnew file mode 100755 index 00000000000..e65ca1297c7 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png Binary files differnew file mode 100755 index 00000000000..7c9fa6c6edc --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png Binary files differnew file mode 100755 index 00000000000..0e05810fffe --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_222222_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_222222_256x240.png Binary files differnew file mode 100755 index 00000000000..b273ff111d2 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_222222_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_2e83ff_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_2e83ff_256x240.png Binary files differnew file mode 100755 index 00000000000..09d1cdc856c --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_2e83ff_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_454545_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_454545_256x240.png Binary files differnew file mode 100755 index 00000000000..59bd45b907c --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_454545_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_888888_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_888888_256x240.png Binary files differnew file mode 100755 index 00000000000..6d02426c114 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_888888_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_cd0a0a_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_cd0a0a_256x240.png Binary files differnew file mode 100755 index 00000000000..2ab019b73ec --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_cd0a0a_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_f6cf3b_256x240.png b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_f6cf3b_256x240.png Binary files differnew file mode 100644 index 00000000000..c9869351a73 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/images/ui-icons_f6cf3b_256x240.png diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css new file mode 100644 index 00000000000..f77de26ed99 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css @@ -0,0 +1,1319 @@ +/*! + * jQuery UI Bootstrap (0.22) + * http://addyosmani.github.com/jquery-ui-bootstrap + * + * Copyright 2012, Addy Osmani + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Portions copyright jQuery UI & Twitter Bootstrap + */ + + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.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; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.16 + * + * 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/?ctl=themeroller + */ + + +/* Component containers +----------------------------------*/ +.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-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 { + font-weight:bold; + 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); + border:1px solid #666; + + } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + + background-color: #e6e6e6; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); + background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + + color: #333; + font-size: 13px; + line-height: normal; + border: 1px solid #ccc; + border-bottom-color: #bbb; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -webkit-transition: 0.1s linear background-image; + -moz-transition: 0.1s linear background-image; + -ms-transition: 0.1s linear background-image; + -o-transition: 0.1s linear background-image; + transition: 0.1s linear background-image; + overflow: visible; + + } + + +.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 { + background-position: 0 -15px; + color: #333; + text-decoration: none; + + + } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; 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 +----------------------------------*/ + + +.ui-state-highlight p, .ui-state-error p, .ui-state-default p{ + font-size: 13px; + font-weight: normal; + line-height: 18px; + margin:7px 15px; +} +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { + + + position: relative; + margin-bottom: 18px; + color: #404040; + background-color: #eedc94; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94)); + background-image: -moz-linear-gradient(top, #fceec1, #eedc94); + background-image: -ms-linear-gradient(top, #fceec1, #eedc94); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94)); + background-image: -webkit-linear-gradient(top, #fceec1, #eedc94); + background-image: -o-linear-gradient(top, #fceec1, #eedc94); + background-image: linear-gradient(top, #fceec1, #eedc94); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #eedc94 #eedc94 #e4c652; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + border-width: 1px; + border-style: solid; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + + +} +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { + + + position: relative; + margin-bottom: 18px; + color: #ffffff; + border-width: 1px; + border-style: solid; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + background-color: #c43c35; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #c43c35 #c43c35 #882a25; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + + +} +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.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; } + + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_f6cf3b_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.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-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.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.16 + * + * 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.16 + * + * 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.16 + * + * 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; 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-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.16 + * + * 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.16 + * + * 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; + background:#0064CD; + color:#fff +} + + +/* + * jQuery UI Button 1.8.16 + * + * 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 { + + cursor: pointer; + display: inline-block; + background-color: #e6e6e6; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); + background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + padding: 5px 14px 6px; + margin: 0; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + color: #333; + font-size: 13px; + line-height: normal; + border: 1px solid #ccc; + border-bottom-color: #bbb; + + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -webkit-transition: 0.1s linear background-image; + -moz-transition: 0.1s linear background-image; + -ms-transition: 0.1s linear background-image; + -o-transition: 0.1s linear background-image; + transition: 0.1s linear background-image; + overflow: visible; + +} /* the overflow property removes extra width in IE */ + +.ui-button-primary { + color: #ffffff; + 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-button-success{ + color:#ffffff; + background-color: #57a957; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957)); + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -ms-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(top, #62c462, #57a957); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #57a957 #57a957 #3d773d; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} + +.ui-button-error{ + color:#ffffff; + background-color: #c43c35; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border-color: #c43c35 #c43c35 #882a25; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} + +.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 { } /* 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; } +.ui-button-text-only .ui-button-text { } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; /*tempfix*/ display:none;} +.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 { top: 50%; margin-top:-3px; margin-bottom:3px; } +.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-state-active { + color: #ffffff; + 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-buttonset .ui-button { margin-left: 0; margin-right: -.4em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ + + + +/* + * jQuery UI Dialog 1.8.16 + * + * 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; + padding:5px 15px; + + border:0px 0px 0px 1px solid; + border-color: white; + padding: 5px 15px; + font-size: 18px; + text-decoration:none; + background:none; + -moz-border-radius-bottomright: 0px; + -webkit-border-bottom-right-radius: 0px; + -khtml-border-bottom-right-radius: 0px; + + -moz-border-radius-bottomleft: 0px; + -webkit-border-bottom-left-radius: 0px; + -khtml-border-bottom-left-radius: 0px; + border-bottom-left-radius: 0px; + + border-bottom:1px solid #ccc; + +} +.ui-dialog .ui-dialog-title { + float: left; + color:#404040; + font-weight:bold; + margin-top:5px; + margin-bottom:5px; + padding:5px; + +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 19px; + margin: -10px 0 0 0; + padding: 1px; + height: 18px; + font-size: 20px; + font-weight: bold; + line-height: 13.5px; + text-shadow: 0 1px 0 #ffffff; + filter: alpha(opacity=25); + -khtml-opacity: 0.25; + -moz-opacity: 0.25; + opacity: 0.25; +} + +.ui-dialog .ui-dialog-titlebar-close span { + display: block; + margin: 1px; + text-indent: 9999px; +} + +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; filter: alpha(opacity=90); + -khtml-opacity: 0.90; + -moz-opacity: 0.90; + opacity: 0.90; } + +.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; + background-color: #f5f5f5; + padding: 5px 15px 5px; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + zoom: 1; + margin-bottom: 0; + +} +.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-dialog-buttonpane .ui-dialog-buttonset .ui-button{ + color: #ffffff; + 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); +} +/* + * jQuery UI Slider 1.8.16 + * + * 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; + + color: #ffffff; + 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-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.16 + * + * 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 .ui-tabs-nav{ background:none; border-color: #ddd; + border-style: solid; + border-width: 0 0 1px;} +.ui-tabs { position: relative; padding: .2em; zoom: 1; border:0px;} /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + + +.ui-tabs .ui-tabs-nav li:hover, .ui-tabs .ui-tabs-nav li a:hover{ + background:whiteSmoke; + border-bottom:1px solid #ddd; + padding-bottom:0px; + 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-nav .ui-state-default{ + -webkit-box-shadow: 0px 0px 0px #ffffff; /* Saf3-4, iOS 4.0.2 - 4.2, Android 2.3+ */ + -moz-box-shadow: 0px 0px 0px #ffffff; /* FF3.5 - 3.6 */ + box-shadow: 0px 0px 0px #ffffff; /* Opera 10.5, IE9, FF4+, Chrome 6+, iOS 5 */ +} +.ui-tabs .ui-tabs-nav li a { + + float: left; + text-decoration: none; + cursor: text; + padding: 0 15px; + margin-right: 2px; + line-height: 34px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; + + + } +.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 { + + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: #ffffff; + cursor: default; + color:gray; + outline:none; +} + + +.ui-tabs .ui-tabs-nav li.ui-tabs-selected: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;} +/* 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;} +.ui-tabs .ui-tabs-hide { display: none !important; } + + +/* IE fix for background inheritance from ui-widget*/ +.ui-tabs .ui-tabs-nav li{ + filter:none; +} + + + +/* + * jQuery UI Datepicker 1.8.16 + * + * 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; border:0px; font-weight: bold; width: 100%; padding: 4px 0; background-color: #f5f5f5; color: #808080; } +.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*/ +} + +.ui-datepicker th{ + font-weight: bold; + color: gray; +} + +.ui-datepicker-today a:hover{ + background-color: #808080; + color: #ffffff; + +} +.ui-datepicker-today a{ + background-color: #BFBFBF; + cursor: pointer; + padding: 0 4px; + margin-bottom:0px; + +} + + +.ui-datepicker td a{ + margin-bottom:0px; + border:0px; +} + +.ui-datepicker td:hover{ + color:white; +} + +.ui-datepicker td .ui-state-default { + border:0px; + background:none; + margin-bottom:0px; + padding:5px; + color:gray; + text-align: center; + filter:none; +} + + +.ui-datepicker td .ui-state-active{ + background:#BFBFBF; + margin-bottom:0px; + font-size:normal; + text-shadow: 0px; + color:white; + -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; +} + + +/* + * jQuery UI Progressbar 1.8.16 + * + * 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; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; + +/*this can be removed if ui-widget-header is blue*/ + color: #ffffff; + 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); + } + + + +/*** Input field styling from Bootstrap **/ + 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; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); +} +input:focus, textarea:focus { + outline: 0; + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + -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 { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: 1px dotted #666; +}*/ + +/*input[type="text"], +input[type="password"], +textarea,*/ +.uneditable-input { + display: inline-block; + padding: 4px; + font-size: 13px; + line-height: 18px; + color: #808080; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + + + +/**Toolbar**/ + +.ui-toolbar{ + padding: 7px 14px; + margin: 0 0 18px; + background-color: #f5f5f5; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5)); + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + + +/***Dialog fixes**/ + +.ui-dialog-buttonset .ui-button:nth-child(2){ + cursor: pointer; + display: inline-block; + background-color: #e6e6e6; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); + background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + padding: 5px 14px 6px; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + color: #333; + font-size: 13px; + line-height: normal; + border: 1px solid #ccc; + border-bottom-color: #bbb; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -webkit-transition: 0.1s linear all; + -moz-transition: 0.1s linear all; + -ms-transition: 0.1s linear all; + -o-transition: 0.1s linear all; + transition: 0.1s linear all; + overflow: visible; +} + + + +/***Wijmo Theming**/ + +div.wijmo-wijmenu{ + padding:0 20px; + background-color: #222; + background-color: #222222; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} + +.wijmo-wijmenu .ui-state-default{ + box-shadow: none; + color:#BFBFBF; +} + +.wijmo-wijmenu .ui-state-default .wijmo-wijmenu-text{ + color:#BFBFBF; +} + +.wijmo-wijmenu .ui-state-hover{ + background: #444; + background: rgba(255, 255, 255, 0.05); +} + +.wijmo-wijmenu .ui-state-hover .wijmo-wijmenu-text{ + color:#ffffff; +} + +div.wijmo-wijmenu .ui-widget-header h3{ + position: relative; + margin-top:1px; + padding:0; +} + +.wijmo-wijmenu h3 a{ + color: #FFFFFF; + display: block; + float: left; + font-size: 20px; + font-weight: 200; + line-height: 1; + margin-left: -20px; + margin-top:1px; + padding: 8px 20px 12px; +} + +.wijmo-wijmenu h3 a:hover{ + background-color: rgba(255, 255, 255, 0.05); + color: #FFFFFF; + text-decoration: none; +} + +.wijmo-wijmenu .ui-widget-header{ + border:0px; +} + +.wijmo-wijmenu .wijmo-wijmenu-parent .wijmo-wijmenu-child{ + padding: 0.3em 0; +} + +div.wijmo-wijmenu .wijmo-wijmenu-item .wijmo-wijmenu-child{ + background: #333; + border:0; + margin:0; + padding: 6px 0; + width:160px; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +div.wijmo-wijmenu .wijmo-wijmenu-item{ + margin:0; + border:0; +} + +.wijmo-wijmenu a.wijmo-wijmenu-link{ + margin:0; + line-height: 19px; + padding: 10px 10px 11px; + border:0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius:0; +} + +div.wijmo-wijmenu .wijmo-wijmenu-child .wijmo-wijmenu-link{ + display:block; + float:none; + padding: 4px 15px; + width:auto; +} + +div.wijmo-wijmenu .wijmo-wijmenu-child .wijmo-wijmenu-text +{ + float:none; +} + +.wijmo-wijmenu .wijmo-wijmenu-item .wijmo-wijmenu-child .ui-state-hover { + background: #191919; +} + +.wijmo-wijmenu .wijmo-wijmenu-item .wijmo-wijmenu-separator{ + padding: 5px 0; + background-image: none; + background-color: #222; + border-top: 1px solid #444; + border-bottom:0; + border-left:0; + border-right:0; +} + +.wijmo-wijmenu .wijmo-wijmenu-item input { + -moz-transition: none 0s ease 0s; + background-color: rgba(255, 255, 255, 0.3); + border: 1px solid #111111; + border-radius: 4px 4px 4px 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset, 0 1px 0 rgba(255, 255, 255, 0.25); + color: rgba(255, 255, 255, 0.75); + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + line-height: 1; + margin: 5px 10px 0 10px; + padding: 4px 9px; + width:100px; +} + +.wijmo-wijmenu .wijmo-wijmenu-item input:hover { + background-color: rgba(255, 255, 255, 0.5); + color: #FFFFFF; +} + +.wijmo-wijmenu .wijmo-wijmenu-item input:focus { + background-color: #FFFFFF; + border: 0 none; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + color: #404040; + outline: 0 none; + padding: 5px 10px; + text-shadow: 0 1px 0 #FFFFFF; +} + + +.wijmo-wijmenu .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + text-shadow:none; +} + + +.wijmo-wijmenu .ui-state-default{ + box-shadow: none; + color:#BFBFBF; + filter: none; +} + diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery.ui.1.8.16.ie.css b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery.ui.1.8.16.ie.css new file mode 100644 index 00000000000..b24b9fb8ab0 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery.ui.1.8.16.ie.css @@ -0,0 +1,6 @@ + +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-left, .ui-corner-bottom{ border-radius:0px;} +.ui-state-active,.ui-tabs-selected { border-radius:0px;} +.ui-tabs-selected { border-radius:0px;} +.ui-tabs .ui-tabs-nav li{ filter:none;} +.ui-tabs .ui-tabs-nav li a { border-radius:0px; } diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/demo.css b/addons/web/static/lib/jquery.ui.bootstrap/css/demo.css new file mode 100644 index 00000000000..638dbb99fda --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/demo.css @@ -0,0 +1,117 @@ +/*! + * jQuery UI Bootstrap (0.22) + * http://addyosmani.github.com/jquery-ui-bootstrap + * + * Copyright 2012, Addy Osmani + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Portions copyright jQuery UI & Twitter Bootstrap + */ + + +.demoHeaders { + margin-top: 1.5em; + margin-bottom: 1em; +} + +#dialog_link,#modal_link { + padding: .4em 1em .4em 20px; + text-decoration: none; + position: relative; +} + +#dialog_link span.ui-icon, #modal_link span.ui-icon { + margin: 0 5px 0 0; + position: absolute; + left: .2em; + top: 50%; + margin-top: -8px; +} + +ul#icons { + margin: 0; + padding: 0; +} + +ul#icons li { + margin: 2px; + position: relative; + padding: 4px 0; + cursor: pointer; + float: left; + list-style: none; +} + +ul#icons span.ui-icon { + float: left; + margin: 0 4px; +} + +#buttons button { + float: left; + margin-right: 4px; +} + +#components { + margin: 2em 0 1em; + width: 900px; + margin: 0 auto; + padding: 50px; +} + +#dialog2 label, #dialog2 input { + display: block; +} + +#dialog2 label { + margin-top: 0.5em; +} + +#dialog2 input, #dialog2 textarea { + width: 95%; +} + +#tabs2 { + margin-top: 1em; +} + +#tabs2 li .ui-icon-close { + float: left; + margin: 0.4em 0.2em 0 0; + cursor: pointer; +} + +#add_tab { + cursor: pointer; +} + +.span10 { + width: 500px; + margin-right: 10px; +} + +.span4 { + width: 290px; +} + +.ui-widget-overlay { + width: 500px; +} + +.alert-message p{ + line-height:18px; + margin-bottom:18px; +} + +#toolbar { +padding: 10px 4px; +} + +input[type="text"], +input[type="password"], +.ui-autocomplete-input, +textarea, +.uneditable-input{ + width: 210px; + height: 18px; +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/bootstrap.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/bootstrap.less new file mode 100644 index 00000000000..200aba88d0b --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/bootstrap.less @@ -0,0 +1,26 @@ +/*! + * Bootstrap @VERSION + * + * Copyright 2011 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + * Date: @DATE + */ + +// CSS Reset +@import "reset"; + +// Core variables and mixins +@import "variables"; // Modify this for custom colors, font-sizes, etc +@import "mixins"; + +// Grid system and page structure +@import "scaffolding"; + +// Styled patterns and elements +@import "type"; +@import "forms"; +@import "tables"; +@import "patterns";
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/forms.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/forms.less new file mode 100755 index 00000000000..835890f33f4 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/forms.less @@ -0,0 +1,479 @@ +/* Forms.less + * Base styles for various input types, form layouts, and states + * ------------------------------------------------------------- */ + + +// FORM STYLES +// ----------- + +form { + margin-bottom: @baseline; +} + +// Groups of fields with labels on top (legends) +fieldset { + margin-bottom: @baseline; + padding-top: @baseline; + legend { + display: block; + padding-left: 150px; + font-size: @basefont * 1.5; + line-height: 1; + color: @grayDark; + *padding: 0 0 5px 145px; /* IE6-7 */ + *line-height: 1.5; /* IE6-7 */ + } +} + +// Parent element that clears floats and wraps labels and fields together +form .clearfix { + margin-bottom: @baseline; + .clearfix() +} + +// Set font for forms +.bootstrap label, +.bootstrap input, +.bootstrap select, +.bootstrap textarea { + #font > .sans-serif(normal,13px,normal); +} + +// Float labels left +.bootstrap label { + padding-top: 6px; + font-size: @basefont; + line-height: @baseline; + float: left; + width: 130px; + text-align: right; + color: @grayDark; +} + +// Shift over the inside div to align all label's relevant content +.bootstrap form .input { + margin-left: 150px; +} + +// Checkboxs and radio buttons +.bootstrap input[type=checkbox], +.bootstrap input[type=radio] { + cursor: pointer; +} + +// Inputs, Textareas, Selects +.bootstrap input, +.bootstrap textarea, +.bootstrap select, +.uneditable-input { + display: inline-block; + width: 210px; + height: @baseline; + padding: 4px; + font-size: @basefont; + line-height: @baseline; + color: @gray; + border: 1px solid #ccc; + .border-radius(3px); +} + +// remove padding from select +.bootstrap select { + padding: initial; +} + +// mini reset for non-html5 file types +.bootstrap input[type=checkbox], +.bootstrap input[type=radio] { + width: auto; + height: auto; + padding: 0; + margin: 3px 0; + *margin-top: 0; /* IE6-7 */ + line-height: normal; + border: none; +} + +.bootstrap input[type=file] { + background-color: @white; + padding: initial; + border: initial; + line-height: initial; + .box-shadow(none); +} + +.bootstrap input[type=button], +.bootstrap input[type=reset], +.bootstrap input[type=submit] { + width: auto; + height: auto; +} + +.bootstrap select, +.bootstrap input[type=file] { + height: @baseline * 1.5; // In IE7, the height of the select element cannot be changed by height, only font-size + *height: auto; // Reset for IE7 + line-height: @baseline * 1.5; + *margin-top: 4px; /* For IE7, add top margin to align select with labels */ +} + +// Make multiple select elements height not fixed +select[multiple] { + height: inherit; + background-color: @white; // Fixes Chromium bug of unreadable items +} + +textarea { + height: auto; +} + +// For text that needs to appear as an input but should not be an input +.uneditable-input { + background-color: @white; + display: block; + border-color: #eee; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); + cursor: not-allowed; +} + +// Placeholder text gets special styles; can't be bundled together though for some reason +:-moz-placeholder { + color: @grayLight; +} +::-webkit-input-placeholder { + color: @grayLight; +} + +// Focus states +input, +textarea { + @transition: border linear .2s, box-shadow linear .2s; + .transition(@transition); + .box-shadow(inset 0 1px 3px rgba(0,0,0,.1)); +} +input:focus, +textarea:focus { + outline: 0; + border-color: rgba(82,168,236,.8); + @shadow: inset 0 1px 3px rgba(0,0,0,.1), 0 0 8px rgba(82,168,236,.6); + .box-shadow(@shadow); +} +input[type=file]:focus, +input[type=checkbox]:focus, +select:focus { + .box-shadow(none); // override for file inputs + outline: 1px dotted #666; // Selet elements don't get box-shadow styles, so instead we do outline +} + + +// FORM FIELD FEEDBACK STATES +// -------------------------- + +// Mixin for form field states +.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { + // Set the text color + > label, + .help-block, + .help-inline { + color: @textColor; + } + // Style inputs accordingly + input, + textarea { + color: @textColor; + border-color: @borderColor; + &:focus { + border-color: darken(@borderColor, 10%); + .box-shadow(0 0 6px lighten(@borderColor, 20%)); + } + } + // Give a small background color for input-prepend/-append + .input-prepend .add-on, + .input-append .add-on { + color: @textColor; + background-color: @backgroundColor; + border-color: @textColor; + } +} +// Error +form .clearfix.error { + .formFieldState(#b94a48, #ee5f5b, lighten(#ee5f5b, 30%)); +} +// Warning +form .clearfix.warning { + .formFieldState(#c09853, #ccae64, lighten(#CCAE64, 5%)); +} +// Success +form .clearfix.success { + .formFieldState(#468847, #57a957, lighten(#57a957, 30%)); +} + + +// Form element sizes +// TODO v2: remove duplication here and just stick to .input-[size] in light of adding .spanN sizes +.input-mini, +input.mini, +textarea.mini, +select.mini { + width: 60px; +} +.input-small, +input.small, +textarea.small, +select.small { + width: 90px; +} +.input-medium, +input.medium, +textarea.medium, +select.medium { + width: 150px; +} +.input-large, +input.large, +textarea.large, +select.large { + width: 210px; +} +.input-xlarge, +input.xlarge, +textarea.xlarge, +select.xlarge { + width: 270px; +} +.input-xxlarge, +input.xxlarge, +textarea.xxlarge, +select.xxlarge { + width: 530px; +} +textarea.xxlarge { + overflow-y: auto; +} + +// Grid style input sizes +// This is a duplication of the main grid .columns() mixin, but subtracts 10px to account for input padding and border +.formColumns(@columnSpan: 1) { + display: inline-block; + float: none; + width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 10; + margin-left: 0; +} +input, +textarea { + // Default columns + &.span1 { .formColumns(1); } + &.span2 { .formColumns(2); } + &.span3 { .formColumns(3); } + &.span4 { .formColumns(4); } + &.span5 { .formColumns(5); } + &.span6 { .formColumns(6); } + &.span7 { .formColumns(7); } + &.span8 { .formColumns(8); } + &.span9 { .formColumns(9); } + &.span10 { .formColumns(10); } + &.span11 { .formColumns(11); } + &.span12 { .formColumns(12); } + &.span13 { .formColumns(13); } + &.span14 { .formColumns(14); } + &.span15 { .formColumns(15); } + &.span16 { .formColumns(16); } +} + +// Disabled and read-only inputs +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + background-color: #f5f5f5; + border-color: #ddd; + cursor: not-allowed; +} + +// Actions (the buttons) +.actions { + background: #f5f5f5; + margin-top: @baseline; + margin-bottom: @baseline; + padding: (@baseline - 1) 20px @baseline 150px; + border-top: 1px solid #ddd; + .border-radius(0 0 3px 3px); + .secondary-action { + float: right; + a { + line-height: 30px; + &:hover { + text-decoration: underline; + } + } + } +} + +// Help Text +// TODO: Do we need to set basefont and baseline here? +.help-inline, +.help-block { + font-size: @basefont; + line-height: @baseline; + color: @grayLight; +} +.help-inline { + padding-left: 5px; + *position: relative; /* IE6-7 */ + *top: -5px; /* IE6-7 */ +} + +// Big blocks of help text +.help-block { + display: block; + max-width: 600px; +} + +// Inline Fields (input fields that appear as inline objects +.inline-inputs { + color: @gray; + span { + padding: 0 2px 0 1px; + } +} + +// Allow us to put symbols and text within the input field for a cleaner look +.input-prepend, +.input-append { + input { + .border-radius(0 3px 3px 0); + } + .add-on { + position: relative; + background: #f5f5f5; + border: 1px solid #ccc; + z-index: 2; + float: left; + display: block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 4px 4px 5px; + margin-right: -1px; + font-weight: normal; + line-height: 18px; + color: @grayLight; + text-align: center; + text-shadow: 0 1px 0 @white; + .border-radius(3px 0 0 3px); + } + .active { + background: lighten(@green, 30); + border-color: @green; + } +} +.input-prepend { + .add-on { + *margin-top: 1px; /* IE6-7 */ + } +} +.input-append { + input { + float: left; + .border-radius(3px 0 0 3px); + } + .add-on { + .border-radius(0 3px 3px 0); + margin-right: 0; + margin-left: -1px; + } +} + +// Stacked options for forms (radio buttons or checkboxes) +.inputs-list { + margin: 0 0 5px; + width: 100%; + li { + display: block; + padding: 0; + width: 100%; + } + label { + display: block; + float: none; + width: auto; + padding: 0; + margin-left: 20px; + line-height: @baseline; + text-align: left; + white-space: normal; + strong { + color: @gray; + } + small { + font-size: @basefont - 2; + font-weight: normal; + } + } + .inputs-list { + margin-left: 25px; + margin-bottom: 10px; + padding-top: 0; + } + &:first-child { + padding-top: 6px; + } + li + li { + padding-top: 2px; + } + input[type=radio], + input[type=checkbox] { + margin-bottom: 0; + margin-left: -20px; + float: left; + } +} + +// Stacked forms +.form-stacked { + padding-left: 20px; + fieldset { + padding-top: @baseline / 2; + } + legend { + padding-left: 0; + } + label { + display: block; + float: none; + width: auto; + font-weight: bold; + text-align: left; + line-height: 20px; + padding-top: 0; + } + .clearfix { + margin-bottom: @baseline / 2; + div.input { + margin-left: 0; + } + } + .inputs-list { + margin-bottom: 0; + li { + padding-top: 0; + label { + font-weight: normal; + padding-top: 0; + } + } + } + div.clearfix.error { + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + margin-top: 0; + margin-left: -10px; + } + .actions { + margin-left: -20px; + padding-left: 20px; + } +} diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/mixins.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/mixins.less new file mode 100644 index 00000000000..4a05133c650 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/mixins.less @@ -0,0 +1,222 @@ +/* Mixins.less + * Snippets of reusable CSS to develop faster and keep code readable + * ----------------------------------------------------------------- */ + + +// Clearfix for clearing floats like a boss h5bp.com/q +.clearfix() { + zoom: 1; + &:before, + &:after { + display: table; + content: ""; + zoom: 1; + } + &:after { + clear: both; + } +} + +// Center-align a block level element +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} + +// Sizing shortcuts +.size(@height: 5px, @width: 5px) { + height: @height; + width: @width; +} +.square(@size: 5px) { + .size(@size, @size); +} + +// Input placeholder text +.placeholder(@color: @grayLight) { + :-moz-placeholder { + color: @color; + } + ::-webkit-input-placeholder { + color: @color; + } +} + +// Font Stacks +#font { + .shorthand(@weight: normal, @size: 14px, @lineHeight: 20px) { + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .sans-serif(@weight: normal, @size: 14px, @lineHeight: 20px) { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .serif(@weight: normal, @size: 14px, @lineHeight: 20px) { + font-family: "Georgia", Times New Roman, Times, serif; + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .monospace(@weight: normal, @size: 12px, @lineHeight: 20px) { + font-family: "Monaco", Courier New, monospace; + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } +} + +// Grid System +.fixed-container() { + width: @siteWidth; + margin-left: auto; + margin-right: auto; + .clearfix(); +} +.columns(@columnSpan: 1) { + width: (@gridColumnWidth * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)); +} +.offset(@columnOffset: 1) { + margin-left: (@gridColumnWidth * @columnOffset) + (@gridGutterWidth * (@columnOffset - 1)) + @extraSpace; +} +// Necessary grid styles for every column to make them appear next to each other horizontally +.gridColumn() { + display: inline; + float: left; + margin-left: @gridGutterWidth; +} +// makeColumn can be used to mark any element (e.g., .content-primary) as a column without changing markup to .span something +.makeColumn(@columnSpan: 1) { + .gridColumn(); + .columns(@columnSpan); +} + +// Border Radius +.border-radius(@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +// Drop shadows +.box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +// Transitions +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -ms-transition: @transition; + -o-transition: @transition; + transition: @transition; +} + +// Background clipping +.background-clip(@clip) { + -webkit-background-clip: @clip; + -moz-background-clip: @clip; + background-clip: @clip; +} + +// CSS3 Content Columns +.content-columns(@columnCount, @columnGap: 20px) { + -webkit-column-count: @columnCount; + -moz-column-count: @columnCount; + column-count: @columnCount; + -webkit-column-gap: @columnGap; + -moz-column-gap: @columnGap; + column-gap: @columnGap; +} + +// Make any element resizable for prototyping +.resizable(@direction: both) { + resize: @direction; // Options are horizontal, vertical, both + overflow: auto; // Safari fix +} + +// Add an alphatransparency value to any background or border color (via Elyse Holladay) +#translucent { + .background(@color: @white, @alpha: 1) { + background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + } + .border(@color: @white, @alpha: 1) { + border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + background-clip: padding-box; + } +} + +// Gradient Bar Colors for buttons and allerts +.gradientBar(@primaryColor, @secondaryColor) { + #gradient > .vertical(@primaryColor, @secondaryColor); + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); + border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); +} + +// Gradients +#gradient { + .horizontal (@startColor: #555, @endColor: #333) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, right top, from(@startColor), to(@endColor)); // Konqueror + background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ + background-image: -ms-linear-gradient(left, @startColor, @endColor); // IE10 + background-image: -webkit-gradient(linear, left top, right top, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(left, @startColor, @endColor); // Le standard + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",@startColor,@endColor)); // IE9 and down + } + .vertical (@startColor: #555, @endColor: #333) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(@startColor), to(@endColor)); // Konqueror + background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ + background-image: -ms-linear-gradient(top, @startColor, @endColor); // IE10 + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(top, @startColor, @endColor); // The standard + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down + } + .directional (@startColor: #555, @endColor: #333, @deg: 45deg) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ + background-image: -ms-linear-gradient(@deg, @startColor, @endColor); // IE10 + background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(@deg, @startColor, @endColor); // The standard + } + .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: @endColor; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); + background-image: -ms-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down, gets no color-stop at all for proper fallback + } +} + +// Reset filters for IE +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} + +// Opacity +.opacity(@opacity: 100) { + filter: e(%("alpha(opacity=%d)", @opacity)); + -khtml-opacity: @opacity / 100; + -moz-opacity: @opacity / 100; + opacity: @opacity / 100; +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/patterns.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/patterns.less new file mode 100644 index 00000000000..8d1ce36463c --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/patterns.less @@ -0,0 +1,1065 @@ +/* Patterns.less + * Repeatable UI elements outside the base styles provided from the scaffolding + * ---------------------------------------------------------------------------- */ + + +// TOPBAR +// ------ + +// Topbar for Branding and Nav +.topbar { + height: 40px; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 10000; + overflow: visible; + + // Links get text shadow + a { + color: @grayLight; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + } + + // Hover and active states + // h3 for backwards compatibility + h3 a:hover, + .brand:hover, + ul .active > a { + background-color: #333; + background-color: rgba(255,255,255,.05); + color: @white; + text-decoration: none; + } + + // Website name + // h3 left for backwards compatibility + h3 { + position: relative; + } + h3 a, + .brand { + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; // negative indent to left-align the text down the page + color: @white; + font-size: 20px; + font-weight: 200; + line-height: 1; + } + + // Plain text in topbar + p { + margin: 0; + line-height: 40px; + a:hover { + background-color: transparent; + color: @white; + } + } + + // Search Form + form { + float: left; + margin: 5px 0 0 0; + position: relative; + .opacity(100); + } + // Todo: remove from v2.0 when ready, added for legacy + form.pull-right { + float: right; + } + input { + background-color: #444; + background-color: rgba(255,255,255,.3); + #font > .sans-serif(13px, normal, 1); + padding: 4px 9px; + color: @white; + color: rgba(255,255,255,.75); + border: 1px solid #111; + .border-radius(4px); + @shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0px rgba(255,255,255,.25); + .box-shadow(@shadow); + .transition(none); + + // Placeholder text gets special styles; can't be bundled together though for some reason + &:-moz-placeholder { + color: @grayLighter; + } + &::-webkit-input-placeholder { + color: @grayLighter; + } + // Hover states + &:hover { + background-color: @grayLight; + background-color: rgba(255,255,255,.5); + color: @white; + } + // Focus states (we use .focused since IE8 and down doesn't support :focus) + &:focus, + &.focused { + outline: 0; + background-color: @white; + color: @grayDark; + text-shadow: 0 1px 0 @white; + border: 0; + padding: 5px 10px; + .box-shadow(0 0 3px rgba(0,0,0,.15)); + } + } +} + +// gradient is applied to it's own element because overflow visible is not honored by ie when filter is present +// For backwards compatibility, include .topbar .fill +.topbar-inner, +.topbar .fill { + background-color: #222; + #gradient > .vertical(#333, #222); + @shadow: 0 1px 3px rgba(0,0,0,.25), inset 0 -1px 0 rgba(0,0,0,.1); + .box-shadow(@shadow); +} + + +// NAVIGATION +// ---------- + +// Topbar Nav +// ul.nav for all topbar based navigation to avoid inheritance issues and over-specificity +// For backwards compatibility, leave in .topbar div > ul +.topbar div > ul, +.nav { + display: block; + float: left; + margin: 0 10px 0 0; + position: relative; + left: 0; + > li { + display: block; + float: left; + } + a { + display: block; + float: none; + padding: 10px 10px 11px; + line-height: 19px; + text-decoration: none; + &:hover { + color: @white; + text-decoration: none; + } + } + .active > a { + background-color: #222; + background-color: rgba(0,0,0,.5); + } + + // Secondary (floated right) nav in topbar + &.secondary-nav { + float: right; + margin-left: 10px; + margin-right: 0; + // backwards compatibility + .menu-dropdown, + .dropdown-menu { + right: 0; + border: 0; + } + } + // Dropdowns within the .nav + // a.menu:hover and li.open .menu for backwards compatibility + a.menu:hover, + li.open .menu, + .dropdown-toggle:hover, + .dropdown.open .dropdown-toggle { + background: #444; + background: rgba(255,255,255,.05); + } + // .menu-dropdown for backwards compatibility + .menu-dropdown, + .dropdown-menu { + background-color: #333; + // a.menu for backwards compatibility + a.menu, + .dropdown-toggle { + color: @white; + &.open { + background: #444; + background: rgba(255,255,255,.05); + } + } + li a { + color: #999; + text-shadow: 0 1px 0 rgba(0,0,0,.5); + &:hover { + #gradient > .vertical(#292929,#191919); + color: @white; + } + } + .active a { + color: @white; + } + .divider { + background-color: #222; + border-color: #444; + } + } +} + +// For backwards compatibility with new dropdowns, redeclare dropdown link padding +.topbar ul .menu-dropdown li a, +.topbar ul .dropdown-menu li a { + padding: 4px 15px; +} + +// Dropdown Menus +// Use the .menu class on any <li> element within the topbar or ul.tabs and you'll get some superfancy dropdowns +// li.menu for backwards compatibility +li.menu, +.dropdown { + position: relative; +} +// The link that is clicked to toggle the dropdown +// a.menu for backwards compatibility +a.menu:after, +.dropdown-toggle:after { + width: 0; + height: 0; + display: inline-block; + content: "↓"; + text-indent: -99999px; + vertical-align: top; + margin-top: 8px; + margin-left: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid @white; + .opacity(50); +} +// The dropdown menu (ul) +// .menu-dropdown for backwards compatibility +.menu-dropdown, +.dropdown-menu { + background-color: @white; + float: left; + display: none; // None by default, but block on "open" of the menu + position: absolute; + top: 40px; + z-index: 900; + min-width: 160px; + max-width: 220px; + _width: 160px; + margin-left: 0; // override default ul styles + margin-right: 0; + padding: 6px 0; + zoom: 1; // do we need this? + border-color: #999; + border-color: rgba(0,0,0,.2); + border-style: solid; + border-width: 0 1px 1px; + .border-radius(0 0 6px 6px); + .box-shadow(0 2px 4px rgba(0,0,0,.2)); + .background-clip(padding-box); + + // Unfloat any li's to make them stack + li { + float: none; + display: block; + background-color: none; + } + // Dividers (basically an hr) within the dropdown + .divider { + height: 1px; + margin: 5px 0; + overflow: hidden; + background-color: #eee; + border-bottom: 1px solid @white; + } +} + +.topbar .dropdown-menu, +.dropdown-menu { + // Links within the dropdown menu + a { + display: block; + padding: 4px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: @gray; + text-shadow: 0 1px 0 @white; + // Hover state + &:hover, + &.hover { + #gradient > .vertical(#eeeeee, #dddddd); + color: @grayDark; + text-decoration: none; + @shadow: inset 0 1px 0 rgba(0,0,0,.025), inset 0 -1px rgba(0,0,0,.025); + .box-shadow(@shadow); + } + } +} + +// Open state for the dropdown +// .open for backwards compatibility +.open, +.dropdown.open { + // .menu for backwards compatibility + .menu, + .dropdown-toggle { + color: @white; + background: #ccc; + background: rgba(0,0,0,.3); + } + // .menu-dropdown for backwards compatibility + .menu-dropdown, + .dropdown-menu { + display: block; + } +} + + +// TABS AND PILLS +// -------------- + +// Common styles +.tabs, +.pills { + margin: 0 0 @baseline; + padding: 0; + list-style: none; + .clearfix(); + > li { + float: left; + > a { + display: block; + } + } +} + +// Tabs +.tabs { + border-color: #ddd; + border-style: solid; + border-width: 0 0 1px; + > li { + position: relative; // For the dropdowns mostly + margin-bottom: -1px; + > a { + padding: 0 15px; + margin-right: 2px; + line-height: (@baseline * 2) - 2; + border: 1px solid transparent; + .border-radius(4px 4px 0 0); + &:hover { + text-decoration: none; + background-color: #eee; + border-color: #eee #eee #ddd; + } + } + } + // Active state, and it's :hover to override normal :hover + .active > a, + .active > a:hover { + color: @gray; + background-color: @white; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; + } +} + +// Dropdowns in tabs +.tabs { + // first one for backwards compatibility + .menu-dropdown, + .dropdown-menu { + top: 35px; + border-width: 1px; + .border-radius(0 6px 6px 6px); + } + // first one for backwards compatibility + a.menu:after, + .dropdown-toggle:after { + border-top-color: #999; + margin-top: 15px; + margin-left: 5px; + } + // first one for backwards compatibility + li.open.menu .menu, + .open.dropdown .dropdown-toggle { + border-color: #999; + } + // first one for backwards compatibility + li.open a.menu:after, + .dropdown.open .dropdown-toggle:after { + border-top-color: #555; + } +} + +// Pills +.pills { + a { + margin: 5px 3px 5px 0; + padding: 0 15px; + line-height: 30px; + text-shadow: 0 1px 1px @white; + .border-radius(15px); + &:hover { + color: @white; + text-decoration: none; + text-shadow: 0 1px 1px rgba(0,0,0,.25); + background-color: @linkColorHover; + } + } + .active a { + color: @white; + text-shadow: 0 1px 1px rgba(0,0,0,.25); + background-color: @linkColor; + } +} + +// Stacked pills +.pills-vertical > li { + float: none; +} + +// Tabbable areas +.tab-content, +.pill-content { +} +.tab-content > .tab-pane, +.pill-content > .pill-pane, +.tab-content > div, +.pill-content > div { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} + + +// BREADCRUMBS +// ----------- + +.breadcrumb { + padding: 7px 14px; + margin: 0 0 @baseline; + #gradient > .vertical(#ffffff, #f5f5f5); + border: 1px solid #ddd; + .border-radius(3px); + .box-shadow(inset 0 1px 0 @white); + li { + display: inline; + text-shadow: 0 1px 0 @white; + } + .divider { + padding: 0 5px; + color: @grayLight; + } + .active a { + color: @grayDark; + } +} + + +// PAGE HEADERS +// ------------ + +.hero-unit { + background-color: #f5f5f5; + margin-bottom: 30px; + padding: 60px; + .border-radius(6px); + h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + } + p { + font-size: 18px; + font-weight: 200; + line-height: @baseline * 1.5; + } +} +footer { + margin-top: @baseline - 1; + padding-top: @baseline - 1; + border-top: 1px solid #eee; +} + + +// PAGE HEADERS +// ------------ + +.page-header { + margin-bottom: @baseline - 1; + border-bottom: 1px solid #ddd; + .box-shadow(0 1px 0 rgba(255,255,255,.5)); + h1 { + margin-bottom: (@baseline / 2) - 1px; + } +} + + +// BUTTON STYLES +// ------------- + +// Shared colors for buttons and alerts +.btn, +.alert-message { + // Set text color + &.danger, + &.danger:hover, + &.error, + &.error:hover, + &.success, + &.success:hover, + &.info, + &.info:hover { + color: @white + } + // Sets the close button to the middle of message + .close{ + font-family: Arial, sans-serif; + line-height: 18px; + } + // Danger and error appear as red + &.danger, + &.error { + .gradientBar(#ee5f5b, #c43c35); + } + // Success appears as green + &.success { + .gradientBar(#62c462, #57a957); + } + // Info appears as a neutral blue + &.info { + .gradientBar(#5bc0de, #339bb9); + } +} + +// Base .btn styles +.btn { + // Button Base + cursor: pointer; + display: inline-block; + #gradient > .vertical-three-colors(#ffffff, #ffffff, 25%, darken(#ffffff, 10%)); // Don't use .gradientbar() here since it does a three-color gradient + padding: 5px 14px 6px; + text-shadow: 0 1px 1px rgba(255,255,255,.75); + color: #333; + font-size: @basefont; + line-height: normal; + border: 1px solid #ccc; + border-bottom-color: #bbb; + .border-radius(4px); + @shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + .box-shadow(@shadow); + + &:hover { + background-position: 0 -15px; + color: #333; + text-decoration: none; + } + + // Focus state for keyboard and accessibility + &:focus { + outline: 1px dotted #666; + } + + // Primary Button Type + &.primary { + color: @white; + .gradientBar(@blue, @blueDark) + } + + // Transitions + .transition(.1s linear all); + + // Active and Disabled states + &.active, + &:active { + @shadow: inset 0 2px 4px rgba(0,0,0,.25), 0 1px 2px rgba(0,0,0,.05); + .box-shadow(@shadow); + } + &.disabled { + cursor: default; + background-image: none; + .reset-filter(); + .opacity(65); + .box-shadow(none); + } + &[disabled] { + // disabled pseudo can't be included with .disabled + // def because IE8 and below will drop it ;_; + cursor: default; + background-image: none; + .reset-filter(); + .opacity(65); + .box-shadow(none); + } + + // Button Sizes + &.large { + font-size: @basefont + 2px; + line-height: normal; + padding: 9px 14px 9px; + .border-radius(6px); + } + &.small { + padding: 7px 9px 7px; + font-size: @basefont - 2px; + } +} +// Super jank hack for removing border-radius from IE9 so we can keep filter gradients on alerts and buttons +:root .alert-message, +:root .btn { + border-radius: 0 \0; +} + +// Help Firefox not be a jerk about adding extra padding to buttons +button.btn, +input[type=submit].btn { + &::-moz-focus-inner { + padding: 0; + border: 0; + } +} + + +// CLOSE ICONS +// ----------- +.close { + float: right; + color: @black; + font-size: 20px; + font-weight: bold; + line-height: @baseline * .75; + text-shadow: 0 1px 0 rgba(255,255,255,1); + .opacity(25); + &:hover { + color: @black; + text-decoration: none; + .opacity(40); + } +} + + +// ERROR STYLES +// ------------ + +// Base alert styles +.alert-message { + position: relative; + padding: 7px 15px; + margin-bottom: @baseline; + color: @grayDark; + .gradientBar(#fceec1, #eedc94); // warning by default + text-shadow: 0 1px 0 rgba(255,255,255,.5); + border-width: 1px; + border-style: solid; + .border-radius(4px); + .box-shadow(inset 0 1px 0 rgba(255,255,255,.25)); + + // Adjust close icon + .close { + margin-top: 1px; + *margin-top: 0; // For IE7 + } + + // Make links same color as text and stand out more + a { + font-weight: bold; + color: @grayDark; + } + &.danger p a, + &.error p a, + &.success p a, + &.info p a { + color: @white; + } + + // Remove extra margin from content + h5 { + line-height: @baseline; + } + p { + margin-bottom: 0; + } + div { + margin-top: 5px; + margin-bottom: 2px; + line-height: 28px; + } + .btn { + // Provide actions with buttons + .box-shadow(0 1px 0 rgba(255,255,255,.25)); + } + + &.block-message { + background-image: none; + background-color: lighten(#fceec1, 5%); + .reset-filter(); + padding: 14px; + border-color: #fceec1; + .box-shadow(none); + ul, p { + margin-right: 30px; + } + ul { + margin-bottom: 0; + } + li { + color: @grayDark; + } + .alert-actions { + margin-top: 5px; + } + &.error, + &.success, + &.info { + color: @grayDark; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + } + &.error { + background-color: lighten(#f56a66, 25%); + border-color: lighten(#f56a66, 20%); + } + &.success { + background-color: lighten(#62c462, 30%); + border-color: lighten(#62c462, 25%); + } + &.info { + background-color: lighten(#6bd0ee, 25%); + border-color: lighten(#6bd0ee, 20%); + } + // Change link color back + &.danger p a, + &.error p a, + &.success p a, + &.info p a { + color: @grayDark; + } + + } +} + + +// PAGINATION +// ---------- + +.pagination { + height: @baseline * 2; + margin: @baseline 0; + ul { + float: left; + margin: 0; + border: 1px solid #ddd; + border: 1px solid rgba(0,0,0,.15); + .border-radius(3px); + .box-shadow(0 1px 2px rgba(0,0,0,.05)); + } + li { + display: inline; + } + a { + float: left; + padding: 0 14px; + line-height: (@baseline * 2) - 2; + border-right: 1px solid; + border-right-color: #ddd; + border-right-color: rgba(0,0,0,.15); + *border-right-color: #ddd; /* IE6-7 */ + text-decoration: none; + } + a:hover, + .active a { + background-color: lighten(@blue, 45%); + } + .disabled a, + .disabled a:hover { + background-color: transparent; + color: @grayLight; + } + .next a { + border: 0; + } +} + + +// WELLS +// ----- + +.well { + background-color: #f5f5f5; + margin-bottom: 20px; + padding: 19px; + min-height: 20px; + border: 1px solid #eee; + border: 1px solid rgba(0,0,0,.05); + .border-radius(4px); + .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); + blockquote { + border-color: #ddd; + border-color: rgba(0,0,0,.15); + } +} + + +// MODALS +// ------ + +.modal-backdrop { + background-color: @black; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10000; + // Fade for backdrop + &.fade { opacity: 0; } +} + +.modal-backdrop, +.modal-backdrop.fade.in { + .opacity(80); +} + +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 11000; + /* jQueryUI Bootstrap Theme */ + max-height: 500px; + overflow: auto; + /* // jQuery UI Bootstrap Theme */ + width: 560px; + margin: -250px 0 0 -280px; + background-color: @white; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.3); + *border: 1px solid #999; /* IE6-7 */ + .border-radius(6px); + .box-shadow(0 3px 7px rgba(0,0,0,0.3)); + .background-clip(padding-box); + .close { margin-top: 7px; } + &.fade { + .transition(e('opacity .3s linear, top .3s ease-out')); + top: -25%; + } + &.fade.in { top: 50%; } +} +.modal-header { + border-bottom: 1px solid #eee; + padding: 5px 15px; +} +.modal-body { + padding: 15px; +} +.modal-body form { + margin-bottom: 0; +} +.modal-footer { + background-color: #f5f5f5; + padding: 14px 15px 15px; + border-top: 1px solid #ddd; + .border-radius(0 0 6px 6px); + .box-shadow(inset 0 1px 0 @white); + .clearfix(); + margin-bottom: 0; + .btn { + float: right; + margin-left: 5px; + } +} + +// Fix the stacking of these components when in modals +.modal .popover, +.modal .twipsy { + z-index: 12000; +} + + +// POPOVER ARROWS +// -------------- + +#popoverArrow { + .above(@arrowWidth: 5px) { + bottom: 0; + left: 50%; + margin-left: -@arrowWidth; + border-left: @arrowWidth solid transparent; + border-right: @arrowWidth solid transparent; + border-top: @arrowWidth solid @black; + } + .left(@arrowWidth: 5px) { + top: 50%; + right: 0; + margin-top: -@arrowWidth; + border-top: @arrowWidth solid transparent; + border-bottom: @arrowWidth solid transparent; + border-left: @arrowWidth solid @black; + } + .below(@arrowWidth: 5px) { + top: 0; + left: 50%; + margin-left: -@arrowWidth; + border-left: @arrowWidth solid transparent; + border-right: @arrowWidth solid transparent; + border-bottom: @arrowWidth solid @black; + } + .right(@arrowWidth: 5px) { + top: 50%; + left: 0; + margin-top: -@arrowWidth; + border-top: @arrowWidth solid transparent; + border-bottom: @arrowWidth solid transparent; + border-right: @arrowWidth solid @black; + } +} + +// TWIPSY +// ------ + +.twipsy { + display: block; + position: absolute; + visibility: visible; + padding: 5px; + font-size: 11px; + z-index: 1000; + .opacity(80); + &.fade.in { + .opacity(80); + } + &.above .twipsy-arrow { #popoverArrow > .above(); } + &.left .twipsy-arrow { #popoverArrow > .left(); } + &.below .twipsy-arrow { #popoverArrow > .below(); } + &.right .twipsy-arrow { #popoverArrow > .right(); } +} +.twipsy-inner { + padding: 3px 8px; + background-color: @black; + color: white; + text-align: center; + max-width: 200px; + text-decoration: none; + .border-radius(4px); +} +.twipsy-arrow { + position: absolute; + width: 0; + height: 0; +} + + +// POPOVERS +// -------- + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1000; + padding: 5px; + display: none; + &.above .arrow { #popoverArrow > .above(); } + &.right .arrow { #popoverArrow > .right(); } + &.below .arrow { #popoverArrow > .below(); } + &.left .arrow { #popoverArrow > .left(); } + .arrow { + position: absolute; + width: 0; + height: 0; + } + .inner { + background: @black; + background: rgba(0,0,0,.8); + padding: 3px; + overflow: hidden; + width: 280px; + .border-radius(6px); + .box-shadow(0 3px 7px rgba(0,0,0,0.3)); + } + .title { + background-color: #f5f5f5; + padding: 9px 15px; + line-height: 1; + .border-radius(3px 3px 0 0); + border-bottom:1px solid #eee; + } + .content { + background-color: @white; + padding: 14px; + .border-radius(0 0 3px 3px); + .background-clip(padding-box); + p, ul, ol { + margin-bottom: 0; + } + } +} + + +// PATTERN ANIMATIONS +// ------------------ + +.fade { + .transition(opacity .15s linear); + opacity: 0; + &.in { + opacity: 1; + } +} + + +// LABELS +// ------ + +.label { + padding: 1px 3px 2px; + font-size: @basefont * .75; + font-weight: bold; + color: @white; + text-transform: uppercase; + white-space: nowrap; + background-color: @grayLight; + .border-radius(3px); + text-shadow: none; + &.important { background-color: #c43c35; } + &.warning { background-color: @orange; } + &.success { background-color: @green; } + &.notice { background-color: lighten(@blue, 25%); } +} + + +// MEDIA GRIDS +// ----------- + +.media-grid { + margin-left: -@gridGutterWidth; + margin-bottom: 0; + .clearfix(); + li { + display: inline; + } + a { + float: left; + padding: 4px; + margin: 0 0 @baseline @gridGutterWidth; + border: 1px solid #ddd; + .border-radius(4px); + .box-shadow(0 1px 1px rgba(0,0,0,.075)); + img { + display: block; + } + &:hover { + border-color: @linkColor; + .box-shadow(0 1px 4px rgba(0,105,214,.25)); + } + } +} diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/reset.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/reset.less new file mode 100644 index 00000000000..cec44f50c0c --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/reset.less @@ -0,0 +1,142 @@ +/* Reset.less + * Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). + * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ + + +// ERIC MEYER RESET +// -------------------------------------------------- + +html, body { margin: 0; padding: 0; } +h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, cite, code, del, dfn, em, img, q, s, samp, small, strike, strong, sub, sup, tt, var, dd, dl, dt, li, ol, ul, fieldset, form, label, legend, button, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; font-weight: normal; font-style: normal; font-size: 100%; line-height: 1; font-family: inherit; } +table { border-collapse: collapse; border-spacing: 0; } +ol, ul { list-style: none; } +q:before, q:after, blockquote:before, blockquote:after { content: ""; } + + +// Normalize.css +// Pulling in select resets form the normalize.css project +// -------------------------------------------------- + +// Display in IE6-9 and FF3 +// ------------------------- +// Source: http://github.com/necolas/normalize.css +html { + overflow-y: scroll; + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +// Focus states +/*a:focus { + outline: thin dotted; +}*/ +// Hover & Active +a:hover, +a:active { + outline: 0; +} + +// Display in IE6-9 and FF3 +// ------------------------- +// Source: http://github.com/necolas/normalize.css +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +// Display block in IE6-9 and FF3 +// ------------------------- +// Source: http://github.com/necolas/normalize.css +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +// Prevents modern browsers from displaying 'audio' without controls +// ------------------------- +// Source: http://github.com/necolas/normalize.css +audio:not([controls]) { + display: none; +} + +// Prevents sub and sup affecting line-height in all browsers +// ------------------------- +// Source: http://github.com/necolas/normalize.css +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} + +// Img border in a's and image quality +// ------------------------- +// Source: http://github.com/necolas/normalize.css +img { + border: 0; + -ms-interpolation-mode: bicubic; +} + +// Forms +// ------------------------- +// Source: http://github.com/necolas/normalize.css + +// Font size in all browsers, margin changes, misc consistency +.bootstrap button, +.bootstrap input, +.bootstrap select, +.bootstrap textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle; +} +.bootstrap button, +.bootstrap input { + line-height: normal; // FF3/4 have !important on line-height in UA stylesheet + *overflow: visible; // Inner spacing ie IE6/7 +} +.bootstrap button::-moz-focus-inner, +.bootstrap input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 + border: 0; + padding: 0; +} +.bootstrap button, +.bootstrap input[type="button"], +.bootstrap input[type="reset"], +.bootstrap input[type="submit"] { + cursor: pointer; // Cursors on all buttons applied consistently + -webkit-appearance: button; // Style clicable inputs in iOS +} +.bootstrap input[type="search"] { // Appearance in Safari/Chrome + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +.bootstrap input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 +} + +textarea { + overflow: auto; // Remove vertical scrollbar in IE6-9 + vertical-align: top; // Readability and alignment cross-browser +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/scaffolding.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/scaffolding.less new file mode 100644 index 00000000000..e99046253b1 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/scaffolding.less @@ -0,0 +1,139 @@ +/* + * Scaffolding + * Basic and global styles for generating a grid system, structural layout, and page templates + * ------------------------------------------------------------------------------------------- */ + + +// STRUCTURAL LAYOUT +// ----------------- + +body { + background-color: @white; + margin: 0; + #font > .sans-serif(normal,@basefont,@baseline); + color: @grayDark; +} + +// Container (centered, fixed-width layouts) +.container { + .fixed-container(); +} + +// Fluid layouts (left aligned, with sidebar, min- & max-width content) +.container-fluid { + position: relative; + min-width: 940px; + padding-left: 20px; + padding-right: 20px; + .clearfix(); + > .sidebar { + position: absolute; + top: 0; + left: 20px; + width: 220px; + } + // TODO in v2: rename this and .popover .content to be more specific + > .content { + margin-left: 240px; + } +} + + +// BASE STYLES +// ----------- + +// Links +a { + color: @linkColor; + text-decoration: none; + line-height: inherit; + font-weight: inherit; + &:hover { + color: @linkColorHover; + text-decoration: underline; + } +} + +// Quick floats +.pull-right { + float: right; +} +.pull-left { + float: left; +} + +// Toggling content +.hide { + display: none; +} +.show { + display: block; +} + + +// GRID SYSTEM +// ----------- +// To customize the grid system, bring up the variables.less file and change the column count, size, and gutter there + +.row { + .clearfix(); + margin-left: -@gridGutterWidth; +} + +// Find all .span# classes within .row and give them the necessary properties for grid columns (supported by all browsers back to IE7) +// Credit to @dhg for the idea +.row > [class*="span"] { + .gridColumn(); +} + +// Default columns +.span1 { .columns(1); } +.span2 { .columns(2); } +.span3 { .columns(3); } +.span4 { .columns(4); } +.span5 { .columns(5); } +.span6 { .columns(6); } +.span7 { .columns(7); } +.span8 { .columns(8); } +.span9 { .columns(9); } +.span10 { .columns(10); } +.span11 { .columns(11); } +.span12 { .columns(12); } +.span13 { .columns(13); } +.span14 { .columns(14); } +.span15 { .columns(15); } +.span16 { .columns(16); } + +// For optional 24-column grid +.span17 { .columns(17); } +.span18 { .columns(18); } +.span19 { .columns(19); } +.span20 { .columns(20); } +.span21 { .columns(21); } +.span22 { .columns(22); } +.span23 { .columns(23); } +.span24 { .columns(24); } + +// Offset column options +.row { + > .offset1 { .offset(1); } + > .offset2 { .offset(2); } + > .offset3 { .offset(3); } + > .offset4 { .offset(4); } + > .offset5 { .offset(5); } + > .offset6 { .offset(6); } + > .offset7 { .offset(7); } + > .offset8 { .offset(8); } + > .offset9 { .offset(9); } + > .offset10 { .offset(10); } + > .offset11 { .offset(11); } + > .offset12 { .offset(12); } +} + +// Unique column sizes for 16-column grid +.span-one-third { width: 300px; } +.span-two-thirds { width: 620px; } +.row { + > .offset-one-third { margin-left: 340px; } + > .offset-two-thirds { margin-left: 660px; } +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/tables.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/tables.less new file mode 100644 index 00000000000..6145c9483c4 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/tables.less @@ -0,0 +1,224 @@ +/* + * Tables.less + * Tables for, you guessed it, tabular data + * ---------------------------------------- */ + + +// BASELINE STYLES +// --------------- + +table { + width: 100%; + margin-bottom: @baseline; + padding: 0; + font-size: @basefont; + border-collapse: collapse; + th, + td { + padding: 10px 10px 9px; + line-height: @baseline; + text-align: left; + } + th { + padding-top: 9px; + font-weight: bold; + vertical-align: middle; + } + td { + vertical-align: top; + border-top: 1px solid #ddd; + } + // When scoped to row, fix th in tbody + tbody th { + border-top: 1px solid #ddd; + vertical-align: top; + } +} + + +// CONDENSED VERSION +// ----------------- +.condensed-table { + th, + td { + padding: 5px 5px 4px; + } +} + + +// BORDERED VERSION +// ---------------- + +.bordered-table { + border: 1px solid #ddd; + border-collapse: separate; // Done so we can round those corners! + *border-collapse: collapse; /* IE7, collapse table to remove spacing */ + .border-radius(4px); + th + th, + td + td, + th + td { + border-left: 1px solid #ddd; + } + thead tr:first-child th:first-child, + tbody tr:first-child td:first-child { + .border-radius(4px 0 0 0); + } + thead tr:first-child th:last-child, + tbody tr:first-child td:last-child { + .border-radius(0 4px 0 0); + } + tbody tr:last-child td:first-child { + .border-radius(0 0 0 4px); + } + tbody tr:last-child td:last-child { + .border-radius(0 0 4px 0); + } +} + + +// TABLE CELL SIZES +// ---------------- + +// This is a duplication of the main grid .columns() mixin, but subtracts 20px to account for input padding and border +.tableColumns(@columnSpan: 1) { + width: ((@gridColumnWidth - 20) * @columnSpan) + ((@gridColumnWidth - 20) * (@columnSpan - 1)); +} +table { + // Default columns + .span1 { .tableColumns(1); } + .span2 { .tableColumns(2); } + .span3 { .tableColumns(3); } + .span4 { .tableColumns(4); } + .span5 { .tableColumns(5); } + .span6 { .tableColumns(6); } + .span7 { .tableColumns(7); } + .span8 { .tableColumns(8); } + .span9 { .tableColumns(9); } + .span10 { .tableColumns(10); } + .span11 { .tableColumns(11); } + .span12 { .tableColumns(12); } + .span13 { .tableColumns(13); } + .span14 { .tableColumns(14); } + .span15 { .tableColumns(15); } + .span16 { .tableColumns(16); } +} + + +// ZEBRA-STRIPING +// -------------- + +// Default zebra-stripe styles (alternating gray and transparent backgrounds) +.zebra-striped { + tbody { + tr:nth-child(odd) td, + tr:nth-child(odd) th { + background-color: #f9f9f9; + } + tr:hover td, + tr:hover th { + background-color: #f5f5f5; + } + } +} + +table { + // Tablesorting styles w/ jQuery plugin + .header { + cursor: pointer; + &:after { + content: ""; + float: right; + margin-top: 7px; + border-width: 0 4px 4px; + border-style: solid; + border-color: #000 transparent; + visibility: hidden; + } + } + // Style the sorted column headers (THs) + .headerSortUp, + .headerSortDown { + background-color: rgba(141,192,219,.25); + text-shadow: 0 1px 1px rgba(255,255,255,.75); + } + // Style the ascending (reverse alphabetical) column header + .header:hover { + &:after { + visibility:visible; + } + } + // Style the descending (alphabetical) column header + .headerSortDown, + .headerSortDown:hover { + &:after { + visibility:visible; + .opacity(60); + } + } + // Style the ascending (reverse alphabetical) column header + .headerSortUp { + &:after { + border-bottom: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #000; + visibility:visible; + .box-shadow(none); //can't add boxshadow to downward facing arrow :( + .opacity(60); + } + } + // Blue Table Headings + .blue { + color: @blue; + border-bottom-color: @blue; + } + .headerSortUp.blue, + .headerSortDown.blue { + background-color: lighten(@blue, 40%); + } + // Green Table Headings + .green { + color: @green; + border-bottom-color: @green; + } + .headerSortUp.green, + .headerSortDown.green { + background-color: lighten(@green, 40%); + } + // Red Table Headings + .red { + color: @red; + border-bottom-color: @red; + } + .headerSortUp.red, + .headerSortDown.red { + background-color: lighten(@red, 50%); + } + // Yellow Table Headings + .yellow { + color: @yellow; + border-bottom-color: @yellow; + } + .headerSortUp.yellow, + .headerSortDown.yellow { + background-color: lighten(@yellow, 40%); + } + // Orange Table Headings + .orange { + color: @orange; + border-bottom-color: @orange; + } + .headerSortUp.orange, + .headerSortDown.orange { + background-color: lighten(@orange, 40%); + } + // Purple Table Headings + .purple { + color: @purple; + border-bottom-color: @purple; + } + .headerSortUp.purple, + .headerSortDown.purple { + background-color: lighten(@purple, 40%); + } +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/type.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/type.less new file mode 100644 index 00000000000..077ae9d8f71 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/type.less @@ -0,0 +1,187 @@ +/* Typography.less + * Headings, body text, lists, code, and more for a versatile and durable typography system + * ---------------------------------------------------------------------------------------- */ + + +// BODY TEXT +// --------- + +p { + #font > .shorthand(normal,@basefont,@baseline); + margin-bottom: @baseline / 2; + small { + font-size: @basefont - 2; + color: @grayLight; + } +} + + +// HEADINGS +// -------- + +h1, h2, h3, h4, h5, h6 { + font-weight: bold; + color: @grayDark; + small { + color: @grayLight; + } +} +h1 { + margin-bottom: @baseline; + font-size: 30px; + line-height: @baseline * 2; + small { + font-size: 18px; + } +} +h2 { + font-size: 24px; + line-height: @baseline * 2; + small { + font-size: 14px; + } +} +h3, h4, h5, h6 { + line-height: @baseline * 2; +} +h3 { + font-size: 18px; + small { + font-size: 14px; + } +} +h4 { + font-size: 16px; + small { + font-size: 12px; + } +} +h5 { + font-size: 14px; +} +h6 { + font-size: 13px; + color: @grayLight; + text-transform: uppercase; +} + + +// COLORS +// ------ + +// Unordered and Ordered lists +ul, ol { + margin: 0 0 @baseline 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +ul { + list-style: disc; +} +ol { + list-style: decimal; +} +li { + line-height: @baseline; + color: @gray; +} +ul.unstyled { + list-style: none; + margin-left: 0; +} + +// Description Lists +dl { + margin-bottom: @baseline; + dt, dd { + line-height: @baseline; + } + dt { + font-weight: bold; + } + dd { + margin-left: @baseline / 2; + } +} + +// MISC +// ---- + +// Horizontal rules +hr { + margin: 20px 0 19px; + border: 0; + border-bottom: 1px solid #eee; +} + +// Emphasis +strong { + font-style: inherit; + font-weight: bold; +} +em { + font-style: italic; + font-weight: inherit; + line-height: inherit; +} +.muted { + color: @grayLight; +} + +// Blockquotes +blockquote { + margin-bottom: @baseline; + border-left: 5px solid #eee; + padding-left: 15px; + p { + #font > .shorthand(300,14px,@baseline); + margin-bottom: 0; + } + small { + display: block; + #font > .shorthand(300,12px,@baseline); + color: @grayLight; + &:before { + content: '\2014 \00A0'; + } + } +} + +// Addresses +address { + display: block; + line-height: @baseline; + margin-bottom: @baseline; +} + +// Inline and block code styles +code, pre { + padding: 0 3px 2px; + font-family: Monaco, Andale Mono, Courier New, monospace; + font-size: 12px; + .border-radius(3px); +} +code { + background-color: lighten(@orange, 40%); + color: rgba(0,0,0,.75); + padding: 1px 3px; +} +pre { + background-color: #f5f5f5; + display: block; + padding: (@baseline - 1) / 2; + margin: 0 0 @baseline; + line-height: @baseline; + font-size: 12px; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.15); + .border-radius(3px); + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + +}
\ No newline at end of file diff --git a/addons/web/static/lib/jquery.ui.bootstrap/css/less/variables.less b/addons/web/static/lib/jquery.ui.bootstrap/css/less/variables.less new file mode 100644 index 00000000000..34000d06699 --- /dev/null +++ b/addons/web/static/lib/jquery.ui.bootstrap/css/less/variables.less @@ -0,0 +1,60 @@ +/* Variables.less + * Variables to customize the look and feel of Bootstrap + * ----------------------------------------------------- */ + + +// Links +@linkColor: #0069d6; +@linkColorHover: darken(@linkColor, 15); + +// Grays +@black: #000; +@grayDark: lighten(@black, 25%); +@gray: lighten(@black, 50%); +@grayLight: lighten(@black, 75%); +@grayLighter: lighten(@black, 90%); +@white: #fff; + +// Accent Colors +@blue: #049CDB; +@blueDark: #0064CD; +@green: #46a546; +@red: #9d261d; +@yellow: #ffc40d; +@orange: #f89406; +@pink: #c3325f; +@purple: #7a43b6; + +// Baseline grid +@basefont: 13px; +@baseline: 18px; + +// Griditude +// Modify the grid styles in mixins.less +@gridColumns: 16; +@gridColumnWidth: 40px; +@gridGutterWidth: 20px; +@extraSpace: (@gridGutterWidth * 2); // For our grid calculations +@siteWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); + +// Color Scheme +// Use this to roll your own color schemes if you like (unused by Bootstrap by default) +@baseColor: @blue; // Set a base color +@complement: spin(@baseColor, 180); // Determine a complementary color +@split1: spin(@baseColor, 158); // Split complements +@split2: spin(@baseColor, -158); +@triad1: spin(@baseColor, 135); // Triads colors +@triad2: spin(@baseColor, -135); +@tetra1: spin(@baseColor, 90); // Tetra colors +@tetra2: spin(@baseColor, -90); +@analog1: spin(@baseColor, 22); // Analogs colors +@analog2: spin(@baseColor, -22); + + + +// More variables coming soon: +// - @basefont to @baseFontSize +// - @baseline to @baseLineHeight +// - @baseFontFamily +// - @primaryButtonColor +// - anything else? File an issue on GitHub
\ No newline at end of file diff --git a/addons/web/static/lib/novajs/src/nova.js b/addons/web/static/lib/novajs/src/nova.js deleted file mode 100644 index b01e8fa8dc1..00000000000 --- a/addons/web/static/lib/novajs/src/nova.js +++ /dev/null @@ -1,484 +0,0 @@ -/* -Copyright (c) 2011, OpenERP S.A. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -nova = (function() { - var lib = {}; - lib.internal = {}; - - /* - * (Almost) unmodified John Resig's inheritance. - * - * Defines The Class object. That object can be used to define and inherit classes using - * the extend() method. - * - * Example: - * - * var Person = nova.Class.extend({ - * init: function(isDancing){ - * this.dancing = isDancing; - * }, - * dance: function(){ - * return this.dancing; - * } - * }); - * - * The init() method act as a constructor. This class can be instancied this way: - * - * var person = new Person(true); - * person.dance(); - * - * The Person class can also be extended again: - * - * var Ninja = Person.extend({ - * init: function(){ - * this._super( false ); - * }, - * dance: function(){ - * // Call the inherited version of dance() - * return this._super(); - * }, - * swingSword: function(){ - * return true; - * } - * }); - * - * When extending a class, each re-defined method can use this._super() to call the previous - * implementation of that method. - */ - /* Simple JavaScript Inheritance - * By John Resig http://ejohn.org/ - * MIT Licensed. - */ - // Inspired by base2 and Prototype - (function(){ - var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; - // The base Class implementation (does nothing) - this.Class = function(){}; - - // Create a new Class that inherits from this class - this.Class.extend = function(prop) { - var _super = this.prototype; - - // Instantiate a base class (but only create the instance, - // don't run the init constructor) - initializing = true; - var prototype = new this(); - initializing = false; - - // Copy the properties over onto the new prototype - for (var name in prop) { - // Check if we're overwriting an existing function - prototype[name] = typeof prop[name] == "function" && - typeof _super[name] == "function" && fnTest.test(prop[name]) ? - (function(name, fn){ - return function() { - var tmp = this._super; - - // Add a new ._super() method that is the same method - // but on the super-class - this._super = _super[name]; - - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; - - return ret; - }; - })(name, prop[name]) : - prop[name]; - } - - // The dummy class constructor - function Class() { - // All construction is actually done in the init method - if ( !initializing && this.init ) - this.init.apply(this, arguments); - } - - // Populate our constructed prototype object - Class.prototype = prototype; - - // Enforce the constructor to be what we expect - Class.prototype.constructor = Class; - - // And make this class extendable - for(el in this) { - Class[el] = this[el]; - } - - return Class; - }; - }).call(lib); - // end of John Resig's code - - /** - * Mixin to express the concept of destroying an object. - * When an object is destroyed, it should release any resource - * it could have reserved before. - */ - lib.DestroyableMixin = { - init: function() { - this.__destroyableDestroyed = false; - }, - /** - * Returns true if destroy() was called on the current object. - */ - isDestroyed : function() { - return this.__destroyableDestroyed; - }, - /** - * Inform the object it should destroy itself, releasing any - * resource it could have reserved. - */ - destroy : function() { - this.__destroyableDestroyed = true; - } - }; - - /** - * Mixin to structure objects' life-cycles folowing a parent-children - * relationship. Each object can a have a parent and multiple children. - * When an object is destroyed, all its children are destroyed too. - */ - lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, { - __parentedMixin : true, - init: function() { - lib.DestroyableMixin.init.call(this); - this.__parentedChildren = []; - this.__parentedParent = null; - }, - /** - * Set the parent of the current object. When calling this method, the - * parent will also be informed and will return the current object - * when its getChildren() method is called. If the current object did - * already have a parent, it is unregistered before, which means the - * previous parent will not return the current object anymore when its - * getChildren() method is called. - */ - setParent : function(parent) { - if (this.getParent()) { - if (this.getParent().__parentedMixin) { - this.getParent().__parentedChildren = _.without(this - .getParent().getChildren(), this); - } - } - this.__parentedParent = parent; - if (parent && parent.__parentedMixin) { - parent.__parentedChildren.push(this); - } - }, - /** - * Return the current parent of the object (or null). - */ - getParent : function() { - return this.__parentedParent; - }, - /** - * Return a list of the children of the current object. - */ - getChildren : function() { - return _.clone(this.__parentedChildren); - }, - destroy : function() { - _.each(this.getChildren(), function(el) { - el.destroy(); - }); - this.setParent(undefined); - lib.DestroyableMixin.destroy.call(this); - } - }); - - /* - * Yes, we steal Backbone's events :) - * - * This class just handle the dispatching of events, it is not meant to be extended, - * nor used directly. All integration with parenting and automatic unregistration of - * events is done in EventDispatcherMixin. - */ - // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. - // Backbone may be freely distributed under the MIT license. - // For all details and documentation: - // http://backbonejs.org - lib.internal.Events = lib.Class.extend({ - - on : function(events, callback, context) { - var ev; - events = events.split(/\s+/); - var calls = this._callbacks || (this._callbacks = {}); - while (ev = events.shift()) { - var list = calls[ev] || (calls[ev] = {}); - var tail = list.tail || (list.tail = list.next = {}); - tail.callback = callback; - tail.context = context; - list.tail = tail.next = {}; - } - return this; - }, - - off : function(events, callback, context) { - var ev, calls, node; - if (!events) { - delete this._callbacks; - } else if (calls = this._callbacks) { - events = events.split(/\s+/); - while (ev = events.shift()) { - node = calls[ev]; - delete calls[ev]; - if (!callback || !node) - continue; - while ((node = node.next) && node.next) { - if (node.callback === callback - && (!context || node.context === context)) - continue; - this.on(ev, node.callback, node.context); - } - } - } - return this; - }, - - trigger : function(events) { - var event, node, calls, tail, args, all, rest; - if (!(calls = this._callbacks)) - return this; - all = calls['all']; - (events = events.split(/\s+/)).push(null); - // Save references to the current heads & tails. - while (event = events.shift()) { - if (all) - events.push({ - next : all.next, - tail : all.tail, - event : event - }); - if (!(node = calls[event])) - continue; - events.push({ - next : node.next, - tail : node.tail - }); - } - rest = Array.prototype.slice.call(arguments, 1); - while (node = events.pop()) { - tail = node.tail; - args = node.event ? [ node.event ].concat(rest) : rest; - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, args); - } - } - return this; - } - }); - // end of Backbone's events class - - lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, { - __eventDispatcherMixin: true, - init: function() { - lib.ParentedMixin.init.call(this); - this.__edispatcherEvents = new lib.internal.Events(); - this.__edispatcherRegisteredEvents = []; - }, - on: function(events, dest, func) { - var self = this; - events = events.split(/\s+/); - _.each(events, function(eventName) { - self.__edispatcherEvents.on(eventName, func, dest); - if (dest && dest.__eventDispatcherMixin) { - dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self}); - } - }); - return this; - }, - off: function(events, dest, func) { - var self = this; - events = events.split(/\s+/); - _.each(events, function(eventName) { - self.__edispatcherEvents.off(eventName, func, dest); - if (dest && dest.__eventDispatcherMixin) { - dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) { - return !(el.name === eventName && el.func === func && el.source === self); - }); - } - }); - return this; - }, - trigger: function(events) { - this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments); - return this; - }, - destroy: function() { - var self = this; - _.each(this.__edispatcherRegisteredEvents, function(event) { - event.source.__edispatcherEvents.off(event.name, event.func, self); - }); - this.__edispatcherRegisteredEvents = []; - this.__edispatcherEvents.off(); - lib.ParentedMixin.destroy.call(this); - } - }); - - lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, { - init: function() { - lib.EventDispatcherMixin.init.call(this); - this.__getterSetterInternalMap = {}; - }, - set: function(map) { - var self = this; - var changed = false; - _.each(map, function(val, key) { - var tmp = self.__getterSetterInternalMap[key]; - if (tmp === val) - return; - changed = true; - self.__getterSetterInternalMap[key] = val; - self.trigger("change:" + key, self, { - oldValue: tmp, - newValue: val - }); - }); - if (changed) - self.trigger("change", self); - }, - get: function(key) { - return this.__getterSetterInternalMap[key]; - } - }); - - lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, { - /** - * Tag name when creating a default $element. - * @type string - */ - tagName: 'div', - /** - * Constructs the widget and sets its parent if a parent is given. - * - * @constructs openerp.web.Widget - * @extends openerp.web.CallbackEnabled - * - * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance. - * When that widget is destroyed by calling destroy(), the current instance will be - * destroyed too. Can be null. - * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want - * to bind the current Widget to an already existing part of the DOM, which is not compatible - * with the DOM insertion methods provided by the current implementation of Widget. So - * for new components this argument should not be provided any more. - */ - init: function(parent) { - lib.GetterSetterMixin.init.call(this); - this.$element = $(document.createElement(this.tagName)); - - this.setParent(parent); - }, - /** - * Destroys the current widget, also destroys all its children before destroying itself. - */ - destroy: function() { - _.each(this.getChildren(), function(el) { - el.destroy(); - }); - if(this.$element != null) { - this.$element.remove(); - } - lib.GetterSetterMixin.destroy.call(this); - }, - /** - * Renders the current widget and appends it to the given jQuery object or Widget. - * - * @param target A jQuery object or a Widget instance. - */ - appendTo: function(target) { - var self = this; - return this.__widgetRenderAndInsert(function(t) { - self.$element.appendTo(t); - }, target); - }, - /** - * Renders the current widget and prepends it to the given jQuery object or Widget. - * - * @param target A jQuery object or a Widget instance. - */ - prependTo: function(target) { - var self = this; - return this.__widgetRenderAndInsert(function(t) { - self.$element.prependTo(t); - }, target); - }, - /** - * Renders the current widget and inserts it after to the given jQuery object or Widget. - * - * @param target A jQuery object or a Widget instance. - */ - insertAfter: function(target) { - var self = this; - return this.__widgetRenderAndInsert(function(t) { - self.$element.insertAfter(t); - }, target); - }, - /** - * Renders the current widget and inserts it before to the given jQuery object or Widget. - * - * @param target A jQuery object or a Widget instance. - */ - insertBefore: function(target) { - var self = this; - return this.__widgetRenderAndInsert(function(t) { - self.$element.insertBefore(t); - }, target); - }, - /** - * Renders the current widget and replaces the given jQuery object. - * - * @param target A jQuery object or a Widget instance. - */ - replace: function(target) { - return this.__widgetRenderAndInsert(_.bind(function(t) { - this.$element.replaceAll(t); - }, this), target); - }, - __widgetRenderAndInsert: function(insertion, target) { - this.renderElement(); - insertion(target); - return this.start(); - }, - /** - * This is the method to implement to render the Widget. - */ - renderElement: function() {}, - /** - * Method called after rendering. Mostly used to bind actions, perform asynchronous - * calls, etc... - * - * By convention, the method should return a promise to inform the caller when - * this widget has been initialized. - * - * @returns {jQuery.Deferred} - */ - start: function() {} - })); - - return lib; -})(); diff --git a/addons/web/static/lib/novajs/test/jquery-1.6.4.js b/addons/web/static/lib/novajs/test/jquery-1.6.4.js deleted file mode 100644 index 2c12adbf3e8..00000000000 --- a/addons/web/static/lib/novajs/test/jquery-1.6.4.js +++ /dev/null @@ -1,9046 +0,0 @@ -/*! - * jQuery JavaScript Library v1.6.4 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Mon Sep 12 18:54:48 2011 -0400 - */ -(function( window, undefined ) { - -// Use the correct document accordingly with window argument (sandbox) -var document = window.document, - navigator = window.navigator, - location = window.location; -var jQuery = (function() { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // A central reference to the root jQuery(document) - rootjQuery, - - // A simple way to check for HTML strings or ID strings - // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) - quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - trimLeft = /^\s+/, - trimRight = /\s+$/, - - // Check for digits - rdigit = /\d/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - - // Useragent RegExp - rwebkit = /(webkit)[ \/]([\w.]+)/, - ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, - rmsie = /(msie) ([\w.]+)/, - rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, - - // Matches dashed string for camelizing - rdashAlpha = /-([a-z]|[0-9])/ig, - rmsPrefix = /^-ms-/, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // The deferred used on DOM ready - readyList, - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf, - - // [[Class]] -> type pairs - class2type = {}; - -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context && document.body ) { - this.context = document; - this[0] = document.body; - this.selector = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = quickExpr.exec( selector ); - } - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = (context ? context.ownerDocument || context : document); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; - } - - return jQuery.merge( this, selector ); - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return (context || rootjQuery).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.6.4", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = this.constructor(); - - if ( jQuery.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - jQuery.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); - - // Add the callback - readyList.done( fn ); - - return this; - }, - - eq: function( i ) { - return i === -1 ? - this.slice( i ) : - this.slice( i, +i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).unbind( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyList ) { - return; - } - - readyList = jQuery._Deferred(); - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( jQuery.ready, 1 ); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - // A crude way of determining if an object is a window - isWindow: function( obj ) { - return obj && typeof obj === "object" && "setInterval" in obj; - }, - - isNaN: function( obj ) { - return obj == null || !rdigit.test( obj ) || isNaN( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw msg; - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return (new Function( "return " + data ))(); - - } - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction( object ); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { - break; - } - } - } - } - - return object; - }, - - // Use native String.trim function wherever possible - trim: trim ? - function( text ) { - return text == null ? - "" : - trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = jQuery.type( array ); - - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array ) { - if ( !array ) { - return -1; - } - - if ( indexOf ) { - return indexOf.call( array, elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = [], retVal; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - if ( typeof context === "string" ) { - var tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - var args = slice.call( arguments, 2 ), - proxy = function() { - return fn.apply( context, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - - return proxy; - }, - - // Mutifunctional method to get and set values to a collection - // The value/s can optionally be executed if it's a function - access: function( elems, key, value, exec, fn, pass ) { - var length = elems.length; - - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - jQuery.access( elems, k, key[k], exec, fn, value ); - } - return elems; - } - - // Setting one attribute - if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = !pass && exec && jQuery.isFunction(value); - - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - - return elems; - } - - // Getting an attribute - return length ? fn( elems[0], key ) : undefined; - }, - - now: function() { - return (new Date()).getTime(); - }, - - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = rwebkit.exec( ua ) || - ropera.exec( ua ) || - rmsie.exec( ua ) || - ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - sub: function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); - } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; - }, - - browser: {} -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} - -// IE doesn't match non-breaking spaces with \s -if ( rnotwhite.test( "\xA0" ) ) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -return jQuery; - -})(); - - -var // Promise methods - promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), - // Static reference to slice - sliceDeferred = [].slice; - -jQuery.extend({ - // Create a simple deferred (one callbacks list) - _Deferred: function() { - var // callbacks list - callbacks = [], - // stored [ context , args ] - fired, - // to avoid firing when already doing so - firing, - // flag to know if the deferred has been cancelled - cancelled, - // the deferred itself - deferred = { - - // done( f1, f2, ...) - done: function() { - if ( !cancelled ) { - var args = arguments, - i, - length, - elem, - type, - _fired; - if ( fired ) { - _fired = fired; - fired = 0; - } - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = jQuery.type( elem ); - if ( type === "array" ) { - deferred.done.apply( deferred, elem ); - } else if ( type === "function" ) { - callbacks.push( elem ); - } - } - if ( _fired ) { - deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); - } - } - return this; - }, - - // resolve with given context and args - resolveWith: function( context, args ) { - if ( !cancelled && !fired && !firing ) { - // make sure args are available (#8421) - args = args || []; - firing = 1; - try { - while( callbacks[ 0 ] ) { - callbacks.shift().apply( context, args ); - } - } - finally { - fired = [ context, args ]; - firing = 0; - } - } - return this; - }, - - // resolve with this as context and given arguments - resolve: function() { - deferred.resolveWith( this, arguments ); - return this; - }, - - // Has this deferred been resolved? - isResolved: function() { - return !!( firing || fired ); - }, - - // Cancel - cancel: function() { - cancelled = 1; - callbacks = []; - return this; - } - }; - - return deferred; - }, - - // Full fledged deferred (two callbacks list) - Deferred: function( func ) { - var deferred = jQuery._Deferred(), - failDeferred = jQuery._Deferred(), - promise; - // Add errorDeferred methods, then and promise - jQuery.extend( deferred, { - then: function( doneCallbacks, failCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ); - return this; - }, - always: function() { - return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); - }, - fail: failDeferred.done, - rejectWith: failDeferred.resolveWith, - reject: failDeferred.resolve, - isRejected: failDeferred.isResolved, - pipe: function( fnDone, fnFail ) { - return jQuery.Deferred(function( newDefer ) { - jQuery.each( { - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ] - }, function( handler, data ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( jQuery.isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); - } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( obj == null ) { - if ( promise ) { - return promise; - } - promise = obj = {}; - } - var i = promiseMethods.length; - while( i-- ) { - obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; - } - return obj; - } - }); - // Make sure only one callback list will be used - deferred.done( failDeferred.cancel ).fail( deferred.cancel ); - // Unexpose cancel - delete deferred.cancel; - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - return deferred; - }, - - // Deferred helper - when: function( firstParam ) { - var args = arguments, - i = 0, - length = args.length, - count = length, - deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? - firstParam : - jQuery.Deferred(); - function resolveFunc( i ) { - return function( value ) { - args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - if ( !( --count ) ) { - // Strange bug in FF4: - // Values changed onto the arguments object sometimes end up as undefined values - // outside the $.when method. Cloning the object into a fresh array solves the issue - deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); - } - }; - } - if ( length > 1 ) { - for( ; i < length; i++ ) { - if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc(i), deferred.reject ); - } else { - --count; - } - } - if ( !count ) { - deferred.resolveWith( deferred, args ); - } - } else if ( deferred !== firstParam ) { - deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); - } - return deferred.promise(); - } -}); - - - -jQuery.support = (function() { - - var div = document.createElement( "div" ), - documentElement = document.documentElement, - all, - a, - select, - opt, - input, - marginDiv, - support, - fragment, - body, - testElementParent, - testElement, - testElementStyle, - tds, - events, - eventName, - i, - isSupported; - - // Preliminary tests - div.setAttribute("className", "t"); - div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>"; - - - all = div.getElementsByTagName( "*" ); - a = div.getElementsByTagName( "a" )[ 0 ]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return {}; - } - - // First batch of supports tests - select = document.createElement( "select" ); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName( "input" )[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName( "tbody" ).length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName( "link" ).length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55$/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true - }; - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent( "onclick" ); - } - - // Check if a radio maintains it's value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute("type", "radio"); - support.radioValue = input.value === "t"; - - input.setAttribute("checked", "checked"); - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.firstChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - div.innerHTML = ""; - - // Figure out if the W3C box model works as expected - div.style.width = div.style.paddingLeft = "1px"; - - body = document.getElementsByTagName( "body" )[ 0 ]; - // We use our own, invisible, body unless the body is already present - // in which case we use a div (#9239) - testElement = document.createElement( body ? "div" : "body" ); - testElementStyle = { - visibility: "hidden", - width: 0, - height: 0, - border: 0, - margin: 0, - background: "none" - }; - if ( body ) { - jQuery.extend( testElementStyle, { - position: "absolute", - left: "-1000px", - top: "-1000px" - }); - } - for ( i in testElementStyle ) { - testElement.style[ i ] = testElementStyle[ i ]; - } - testElement.appendChild( div ); - testElementParent = body || documentElement; - testElementParent.insertBefore( testElement, testElementParent.firstChild ); - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - support.boxModel = div.offsetWidth === 2; - - if ( "zoom" in div.style ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.style.display = "inline"; - div.style.zoom = 1; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = ""; - div.innerHTML = "<div style='width:4px;'></div>"; - support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); - } - - div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>"; - tds = div.getElementsByTagName( "td" ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE < 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - div.innerHTML = ""; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( document.defaultView && document.defaultView.getComputedStyle ) { - marginDiv = document.createElement( "div" ); - marginDiv.style.width = "0"; - marginDiv.style.marginRight = "0"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; - } - - // Remove the body element we added - testElement.innerHTML = ""; - testElementParent.removeChild( testElement ); - - // Technique from Juriy Zaytsev - // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for( i in { - submit: 1, - change: 1, - focusin: 1 - } ) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - // Null connected elements to avoid leaks in IE - testElement = fragment = select = opt = body = marginDiv = div = input = null; - - return support; -})(); - -// Keep track of boxModel -jQuery.boxModel = jQuery.support.boxModel; - - - - -var rbrace = /^(?:\{.*\}|\[.*\])$/, - rmultiDash = /([A-Z])/g; - -jQuery.extend({ - cache: {}, - - // Please use with caution - uuid: 0, - - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || (pvt && id && (cache[ id ] && !cache[ id ][ internalKey ]))) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ jQuery.expando ] = id = ++jQuery.uuid; - } else { - id = jQuery.expando; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery - // metadata on plain JS objects when the object is serialized using - // JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); - } else { - cache[ id ] = jQuery.extend(cache[ id ], name); - } - } - - thisCache = cache[ id ]; - - // Internal jQuery data is stored in a separate object inside the object's data - // cache in order to avoid key collisions between internal data and user-defined - // data - if ( pvt ) { - if ( !thisCache[ internalKey ] ) { - thisCache[ internalKey ] = {}; - } - - thisCache = thisCache[ internalKey ]; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should - // not attempt to inspect the internal events object using jQuery.data, as this - // internal data object is undocumented and subject to change. - if ( name === "events" && !thisCache[name] ) { - return thisCache[ internalKey ] && thisCache[ internalKey ].events; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, - - // Reference to internal data cache key - internalKey = jQuery.expando, - - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - - // See jQuery.data for more information - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; - - if ( thisCache ) { - - // Support interoperable removal of hyphenated or camelcased keys - if ( !thisCache[ name ] ) { - name = jQuery.camelCase( name ); - } - - delete thisCache[ name ]; - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !isEmptyDataObject(thisCache) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( pvt ) { - delete cache[ id ][ internalKey ]; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject(cache[ id ]) ) { - return; - } - } - - var internalCache = cache[ id ][ internalKey ]; - - // Browsers that fail expando deletion also refuse to delete expandos on - // the window, but it will allow it on all other JS objects; other browsers - // don't care - // Ensure that `cache` is not a window object #10080 - if ( jQuery.support.deleteExpando || !cache.setInterval ) { - delete cache[ id ]; - } else { - cache[ id ] = null; - } - - // We destroyed the entire user cache at once because it's faster than - // iterating through each key, but we need to continue to persist internal - // data if it existed - if ( internalCache ) { - cache[ id ] = {}; - // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery - // metadata on plain JS objects when the object is serialized using - // JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - - cache[ id ][ internalKey ] = internalCache; - - // Otherwise, we need to eliminate the expando on the node to avoid - // false lookups in the cache for entries that no longer exist - } else if ( isNode ) { - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( jQuery.support.deleteExpando ) { - delete elem[ jQuery.expando ]; - } else if ( elem.removeAttribute ) { - elem.removeAttribute( jQuery.expando ); - } else { - elem[ jQuery.expando ] = null; - } - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - if ( elem.nodeName ) { - var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; - - if ( match ) { - return !(match === true || elem.getAttribute("classid") !== match); - } - } - - return true; - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var data = null; - - if ( typeof key === "undefined" ) { - if ( this.length ) { - data = jQuery.data( this[0] ); - - if ( this[0].nodeType === 1 ) { - var attr = this[0].attributes, name; - for ( var i = 0, l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( this[0], name, data[ name ] ); - } - } - } - } - - return data; - - } else if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - // Try to fetch any internally stored data first - if ( data === undefined && this.length ) { - data = jQuery.data( this[0], key ); - data = dataAttr( this[0], key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - - } else { - return this.each(function() { - var $this = jQuery( this ), - args = [ parts[0], value ]; - - $this.triggerHandler( "setData" + parts[1] + "!", args ); - jQuery.data( this, key, value ); - $this.triggerHandler( "changeData" + parts[1] + "!", args ); - }); - } - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - !jQuery.isNaN( data ) ? parseFloat( data ) : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON -// property to be considered empty objects; this property always exists in -// order to make sure JSON.stringify does not expose internal metadata -function isEmptyDataObject( obj ) { - for ( var name in obj ) { - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - - - - -function handleQueueMarkDefer( elem, type, src ) { - var deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - defer = jQuery.data( elem, deferDataKey, undefined, true ); - if ( defer && - ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && - ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { - // Give room for hard-coded callbacks to fire first - // and eventually mark/queue something else on the element - setTimeout( function() { - if ( !jQuery.data( elem, queueDataKey, undefined, true ) && - !jQuery.data( elem, markDataKey, undefined, true ) ) { - jQuery.removeData( elem, deferDataKey, true ); - defer.resolve(); - } - }, 0 ); - } -} - -jQuery.extend({ - - _mark: function( elem, type ) { - if ( elem ) { - type = (type || "fx") + "mark"; - jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); - } - }, - - _unmark: function( force, elem, type ) { - if ( force !== true ) { - type = elem; - elem = force; - force = false; - } - if ( elem ) { - type = type || "fx"; - var key = type + "mark", - count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); - if ( count ) { - jQuery.data( elem, key, count, true ); - } else { - jQuery.removeData( elem, key, true ); - handleQueueMarkDefer( elem, type, "mark" ); - } - } - }, - - queue: function( elem, type, data ) { - if ( elem ) { - type = (type || "fx") + "queue"; - var q = jQuery.data( elem, type, undefined, true ); - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !q || jQuery.isArray(data) ) { - q = jQuery.data( elem, type, jQuery.makeArray(data), true ); - } else { - q.push( data ); - } - } - return q || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - fn = queue.shift(), - defer; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift("inprogress"); - } - - fn.call(elem, function() { - jQuery.dequeue(elem, type); - }); - } - - if ( !queue.length ) { - jQuery.removeData( elem, type + "queue", true ); - handleQueueMarkDefer( elem, type, "queue" ); - } - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) { - return jQuery.queue( this[0], type ); - } - return this.each(function() { - var queue = jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; - type = type || "fx"; - - return this.queue( type, function() { - var elem = this; - setTimeout(function() { - jQuery.dequeue( elem, type ); - }, time ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, object ) { - if ( typeof type !== "string" ) { - object = type; - type = undefined; - } - type = type || "fx"; - var defer = jQuery.Deferred(), - elements = this, - i = elements.length, - count = 1, - deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - tmp; - function resolve() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - } - while( i-- ) { - if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || - ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || - jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && - jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { - count++; - tmp.done( resolve ); - } - } - resolve(); - return defer.promise(); - } -}); - - - - -var rclass = /[\n\t\r]/g, - rspace = /\s+/, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea)?$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - nodeHook, boolHook; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.attr ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.prop ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { - setClass += classNames[ c ] + " "; - } - } - elem.className = jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classNames, i, l, elem, className, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call(this, j, this.className) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - classNames = (value || "").split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - className = (" " + elem.className + " ").replace( rclass, " " ); - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[ c ] + " ", " "); - } - elem.className = jQuery.trim( className ); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space seperated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " "; - for ( var i = 0, l = this.length; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return undefined; - } - - var isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var self = jQuery(this), val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attrFix: { - // Always normalize to ensure hook usage - tabindex: "tabIndex" - }, - - attr: function( elem, name, value, pass ) { - var nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return undefined; - } - - if ( pass && name in jQuery.attrFn ) { - return jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( !("getAttribute" in elem) ) { - return jQuery.prop( elem, name, value ); - } - - var ret, hooks, - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // Normalize the name if needed - if ( notxml ) { - name = jQuery.attrFix[ name ] || name; - - hooks = jQuery.attrHooks[ name ]; - - if ( !hooks ) { - // Use boolHook for boolean attributes - if ( rboolean.test( name ) ) { - hooks = boolHook; - - // Use nodeHook if available( IE6/7 ) - } else if ( nodeHook ) { - hooks = nodeHook; - } - } - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return undefined; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, "" + value ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, name ) { - var propName; - if ( elem.nodeType === 1 ) { - name = jQuery.attrFix[ name ] || name; - - jQuery.attr( elem, name, "" ); - elem.removeAttribute( name ); - - // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { - elem[ propName ] = false; - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return undefined; - } - - var ret, hooks, - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return (elem[ name ] = value); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Add the tabindex propHook to attrHooks for back-compat -jQuery.attrHooks.tabIndex = jQuery.propHooks.tabIndex; - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode; - return jQuery.prop( elem, name ) === true || ( attrNode = elem.getAttributeNode( name ) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !jQuery.support.getSetAttribute ) { - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - // Return undefined if nodeValue is empty string - return ret && ret.nodeValue !== "" ? - ret.nodeValue : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return (ret.nodeValue = value + ""); - } - }; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); -} - - -// Some attributes require a special call on IE -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return (elem.style.cssText = "" + value); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); - } - } - }); -}); - - - - -var rnamespaces = /\.(.*)$/, - rformElems = /^(?:textarea|input|select)$/i, - rperiod = /\./g, - rspaces = / /g, - rescape = /[^\w\s.|`]/g, - fcleanup = function( nm ) { - return nm.replace(rescape, "\\$&"); - }; - -/* - * A number of helper functions used for managing events. - * Many of the ideas behind this code originated from - * Dean Edwards' addEvent library. - */ -jQuery.event = { - - // Bind an event to an element - // Original by Dean Edwards - add: function( elem, types, handler, data ) { - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - if ( handler === false ) { - handler = returnFalse; - } else if ( !handler ) { - // Fixes bug #7229. Fix recommended by jdalton - return; - } - - var handleObjIn, handleObj; - - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - } - - // Make sure that the function being executed has a unique ID - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure - var elemData = jQuery._data( elem ); - - // If no elemData is found then we must be trying to bind to one of the - // banned noData elements - if ( !elemData ) { - return; - } - - var events = elemData.events, - eventHandle = elemData.handle; - - if ( !events ) { - elemData.events = events = {}; - } - - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.handle.apply( eventHandle.elem, arguments ) : - undefined; - }; - } - - // Add elem as a property of the handle function - // This is to prevent a memory leak with non-native events in IE. - eventHandle.elem = elem; - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = types.split(" "); - - var type, i = 0, namespaces; - - while ( (type = types[ i++ ]) ) { - handleObj = handleObjIn ? - jQuery.extend({}, handleObjIn) : - { handler: handler, data: data }; - - // Namespaced event handlers - if ( type.indexOf(".") > -1 ) { - namespaces = type.split("."); - type = namespaces.shift(); - handleObj.namespace = namespaces.slice(0).sort().join("."); - - } else { - namespaces = []; - handleObj.namespace = ""; - } - - handleObj.type = type; - if ( !handleObj.guid ) { - handleObj.guid = handler.guid; - } - - // Get the current list of functions bound to this event - var handlers = events[ type ], - special = jQuery.event.special[ type ] || {}; - - // Init the event handler queue - if ( !handlers ) { - handlers = events[ type ] = []; - - // Check for a special event handler - // Only use addEventListener/attachEvent if the special - // events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add the function to the element's handler list - handlers.push( handleObj ); - - // Keep track of which events have been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, pos ) { - // don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - if ( handler === false ) { - handler = returnFalse; - } - - var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ), - events = elemData && elemData.events; - - if ( !elemData || !events ) { - return; - } - - // types is actually an event object here - if ( types && types.type ) { - handler = types.handler; - types = types.type; - } - - // Unbind all events for the element - if ( !types || typeof types === "string" && types.charAt(0) === "." ) { - types = types || ""; - - for ( type in events ) { - jQuery.event.remove( elem, type + types ); - } - - return; - } - - // Handle multiple events separated by a space - // jQuery(...).unbind("mouseover mouseout", fn); - types = types.split(" "); - - while ( (type = types[ i++ ]) ) { - origType = type; - handleObj = null; - all = type.indexOf(".") < 0; - namespaces = []; - - if ( !all ) { - // Namespaced event handlers - namespaces = type.split("."); - type = namespaces.shift(); - - namespace = new RegExp("(^|\\.)" + - jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); - } - - eventType = events[ type ]; - - if ( !eventType ) { - continue; - } - - if ( !handler ) { - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( all || namespace.test( handleObj.namespace ) ) { - jQuery.event.remove( elem, origType, handleObj.handler, j ); - eventType.splice( j--, 1 ); - } - } - - continue; - } - - special = jQuery.event.special[ type ] || {}; - - for ( j = pos || 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( handler.guid === handleObj.guid ) { - // remove the given handler for the given type - if ( all || namespace.test( handleObj.namespace ) ) { - if ( pos == null ) { - eventType.splice( j--, 1 ); - } - - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - - if ( pos != null ) { - break; - } - } - } - - // remove generic event handler if no more handlers exist - if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - ret = null; - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - var handle = elemData.handle; - if ( handle ) { - handle.elem = null; - } - - delete elemData.events; - delete elemData.handle; - - if ( jQuery.isEmptyObject( elemData ) ) { - jQuery.removeData( elem, undefined, true ); - } - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Event object or event type - var type = event.type || event, - namespaces = [], - exclusive; - - if ( type.indexOf("!") >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { - // No jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); - - event.type = type; - event.exclusive = exclusive; - event.namespace = namespaces.join("."); - event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); - - // triggerHandler() and global events don't bubble or run the default action - if ( onlyHandlers || !elem ) { - event.preventDefault(); - event.stopPropagation(); - } - - // Handle a global trigger - if ( !elem ) { - // TODO: Stop taunting the data cache; remove global events and always attach to document - jQuery.each( jQuery.cache, function() { - // internalKey variable is just used to make it easier to find - // and potentially change this stuff later; currently it just - // points to jQuery.expando - var internalKey = jQuery.expando, - internalCache = this[ internalKey ]; - if ( internalCache && internalCache.events && internalCache.events[ type ] ) { - jQuery.event.trigger( event, data, internalCache.handle.elem ); - } - }); - return; - } - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // Clean up the event in case it is being reused - event.result = undefined; - event.target = elem; - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? jQuery.makeArray( data ) : []; - data.unshift( event ); - - var cur = elem, - // IE doesn't like method names with a colon (#3533, #8272) - ontype = type.indexOf(":") < 0 ? "on" + type : ""; - - // Fire event on the current element, then bubble up the DOM tree - do { - var handle = jQuery._data( cur, "handle" ); - - event.currentTarget = cur; - if ( handle ) { - handle.apply( cur, data ); - } - - // Trigger an inline bound script - if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { - event.result = false; - event.preventDefault(); - } - - // Bubble up to document, then to window - cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; - } while ( cur && !event.isPropagationStopped() ); - - // If nobody prevented the default action, do it now - if ( !event.isDefaultPrevented() ) { - var old, - special = jQuery.event.special[ type ] || {}; - - if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction)() check here because IE6/7 fails that test. - // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. - try { - if ( ontype && elem[ type ] ) { - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - jQuery.event.triggered = type; - elem[ type ](); - } - } catch ( ieError ) {} - - if ( old ) { - elem[ ontype ] = old; - } - - jQuery.event.triggered = undefined; - } - } - - return event.result; - }, - - handle: function( event ) { - event = jQuery.event.fix( event || window.event ); - // Snapshot the handlers list since a called handler may add/remove events. - var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), - run_all = !event.exclusive && !event.namespace, - args = Array.prototype.slice.call( arguments, 0 ); - - // Use the fix-ed Event rather than the (read-only) native event - args[0] = event; - event.currentTarget = this; - - for ( var j = 0, l = handlers.length; j < l; j++ ) { - var handleObj = handlers[ j ]; - - // Triggered event must 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event. - if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { - // Pass in a reference to the handler function itself - // So that we can later remove it - event.handler = handleObj.handler; - event.data = handleObj.data; - event.handleObj = handleObj; - - var ret = handleObj.handler.apply( this, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - - if ( event.isImmediatePropagationStopped() ) { - break; - } - } - } - return event.result; - }, - - props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // store a copy of the original event object - // and "clone" to set read-only properties - var originalEvent = event; - event = jQuery.Event( originalEvent ); - - for ( var i = this.props.length, prop; i; ) { - prop = this.props[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary - if ( !event.target ) { - // Fixes #1925 where srcElement might not be defined either - event.target = event.srcElement || document; - } - - // check if target is a textnode (safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && event.fromElement ) { - event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; - } - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && event.clientX != null ) { - var eventDocument = event.target.ownerDocument || document, - doc = eventDocument.documentElement, - body = eventDocument.body; - - event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add which for key events - if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { - event.which = event.charCode != null ? event.charCode : event.keyCode; - } - - // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) - if ( !event.metaKey && event.ctrlKey ) { - event.metaKey = event.ctrlKey; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && event.button !== undefined ) { - event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); - } - - return event; - }, - - // Deprecated, use jQuery.guid instead - guid: 1E8, - - // Deprecated, use jQuery.proxy instead - proxy: jQuery.proxy, - - special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady, - teardown: jQuery.noop - }, - - live: { - add: function( handleObj ) { - jQuery.event.add( this, - liveConvert( handleObj.origType, handleObj.selector ), - jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); - }, - - remove: function( handleObj ) { - jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); - } - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - } -}; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - if ( elem.detachEvent ) { - elem.detachEvent( "on" + type, handle ); - } - }; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !this.preventDefault ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // timeStamp is buggy for some events on Firefox(#3843) - // So we won't rely on the native value - this.timeStamp = jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Checks if an event happened on an element within another element -// Used in jQuery.event.special.mouseenter and mouseleave handlers -var withinElement = function( event ) { - - // Check if mouse(over|out) are still within the same parent element - var related = event.relatedTarget, - inside = false, - eventType = event.type; - - event.type = event.data; - - if ( related !== this ) { - - if ( related ) { - inside = jQuery.contains( this, related ); - } - - if ( !inside ) { - - jQuery.event.handle.apply( this, arguments ); - - event.type = eventType; - } - } -}, - -// In case of event delegation, we only need to rename the event.type, -// liveHandler will take care of the rest. -delegate = function( event ) { - event.type = event.data; - jQuery.event.handle.apply( this, arguments ); -}; - -// Create mouseenter and mouseleave events -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - setup: function( data ) { - jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); - }, - teardown: function( data ) { - jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); - } - }; -}); - -// submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function( data, namespaces ) { - if ( !jQuery.nodeName( this, "form" ) ) { - jQuery.event.add(this, "click.specialSubmit", function( e ) { - // Avoid triggering error on non-existent type attribute in IE VML (#7071) - var elem = e.target, - type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : ""; - - if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { - trigger( "submit", this, arguments ); - } - }); - - jQuery.event.add(this, "keypress.specialSubmit", function( e ) { - var elem = e.target, - type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : ""; - - if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { - trigger( "submit", this, arguments ); - } - }); - - } else { - return false; - } - }, - - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialSubmit" ); - } - }; - -} - -// change delegation, happens here so we have bind. -if ( !jQuery.support.changeBubbles ) { - - var changeFilters, - - getVal = function( elem ) { - var type = jQuery.nodeName( elem, "input" ) ? elem.type : "", - val = elem.value; - - if ( type === "radio" || type === "checkbox" ) { - val = elem.checked; - - } else if ( type === "select-multiple" ) { - val = elem.selectedIndex > -1 ? - jQuery.map( elem.options, function( elem ) { - return elem.selected; - }).join("-") : - ""; - - } else if ( jQuery.nodeName( elem, "select" ) ) { - val = elem.selectedIndex; - } - - return val; - }, - - testChange = function testChange( e ) { - var elem = e.target, data, val; - - if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { - return; - } - - data = jQuery._data( elem, "_change_data" ); - val = getVal(elem); - - // the current data will be also retrieved by beforeactivate - if ( e.type !== "focusout" || elem.type !== "radio" ) { - jQuery._data( elem, "_change_data", val ); - } - - if ( data === undefined || val === data ) { - return; - } - - if ( data != null || val ) { - e.type = "change"; - e.liveFired = undefined; - jQuery.event.trigger( e, arguments[1], elem ); - } - }; - - jQuery.event.special.change = { - filters: { - focusout: testChange, - - beforedeactivate: testChange, - - click: function( e ) { - var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; - - if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { - testChange.call( this, e ); - } - }, - - // Change has to be called before submit - // Keydown will be called before keypress, which is used in submit-event delegation - keydown: function( e ) { - var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; - - if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || - (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || - type === "select-multiple" ) { - testChange.call( this, e ); - } - }, - - // Beforeactivate happens also before the previous element is blurred - // with this event you can't trigger a change event, but you can store - // information - beforeactivate: function( e ) { - var elem = e.target; - jQuery._data( elem, "_change_data", getVal(elem) ); - } - }, - - setup: function( data, namespaces ) { - if ( this.type === "file" ) { - return false; - } - - for ( var type in changeFilters ) { - jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); - } - - return rformElems.test( this.nodeName ); - }, - - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialChange" ); - - return rformElems.test( this.nodeName ); - } - }; - - changeFilters = jQuery.event.special.change.filters; - - // Handle when the input is .focus()'d - changeFilters.focus = changeFilters.beforeactivate; -} - -function trigger( type, elem, args ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - // Don't pass args or remember liveFired; they apply to the donor event. - var event = jQuery.extend( {}, args[ 0 ] ); - event.type = type; - event.originalEvent = {}; - event.liveFired = undefined; - jQuery.event.handle.call( elem, event ); - if ( event.isDefaultPrevented() ) { - args[ 0 ].preventDefault(); - } -} - -// Create "bubbling" focus and blur events -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - - function handler( donor ) { - // Donor event is always a native one; fix it and switch its type. - // Let focusin/out handler cancel the donor focus/blur event. - var e = jQuery.event.fix( donor ); - e.type = fix; - e.originalEvent = {}; - jQuery.event.trigger( e, null, e.target ); - if ( e.isDefaultPrevented() ) { - donor.preventDefault(); - } - } - }); -} - -jQuery.each(["bind", "one"], function( i, name ) { - jQuery.fn[ name ] = function( type, data, fn ) { - var handler; - - // Handle object literals - if ( typeof type === "object" ) { - for ( var key in type ) { - this[ name ](key, data, type[key], fn); - } - return this; - } - - if ( arguments.length === 2 || data === false ) { - fn = data; - data = undefined; - } - - if ( name === "one" ) { - handler = function( event ) { - jQuery( this ).unbind( event, handler ); - return fn.apply( this, arguments ); - }; - handler.guid = fn.guid || jQuery.guid++; - } else { - handler = fn; - } - - if ( type === "unload" && name !== "one" ) { - this.one( type, data, fn ); - - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.add( this[i], type, handler, data ); - } - } - - return this; - }; -}); - -jQuery.fn.extend({ - unbind: function( type, fn ) { - // Handle object literals - if ( typeof type === "object" && !type.preventDefault ) { - for ( var key in type ) { - this.unbind(key, type[key]); - } - - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.remove( this[i], type, fn ); - } - } - - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.live( types, data, fn, selector ); - }, - - undelegate: function( selector, types, fn ) { - if ( arguments.length === 0 ) { - return this.unbind( "live" ); - - } else { - return this.die( types, null, fn, selector ); - } - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - - triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -var liveMap = { - focus: "focusin", - blur: "focusout", - mouseenter: "mouseover", - mouseleave: "mouseout" -}; - -jQuery.each(["live", "die"], function( i, name ) { - jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { - var type, i = 0, match, namespaces, preType, - selector = origSelector || this.selector, - context = origSelector ? this : jQuery( this.context ); - - if ( typeof types === "object" && !types.preventDefault ) { - for ( var key in types ) { - context[ name ]( key, data, types[key], selector ); - } - - return this; - } - - if ( name === "die" && !types && - origSelector && origSelector.charAt(0) === "." ) { - - context.unbind( origSelector ); - - return this; - } - - if ( data === false || jQuery.isFunction( data ) ) { - fn = data || returnFalse; - data = undefined; - } - - types = (types || "").split(" "); - - while ( (type = types[ i++ ]) != null ) { - match = rnamespaces.exec( type ); - namespaces = ""; - - if ( match ) { - namespaces = match[0]; - type = type.replace( rnamespaces, "" ); - } - - if ( type === "hover" ) { - types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); - continue; - } - - preType = type; - - if ( liveMap[ type ] ) { - types.push( liveMap[ type ] + namespaces ); - type = type + namespaces; - - } else { - type = (liveMap[ type ] || type) + namespaces; - } - - if ( name === "live" ) { - // bind live handler - for ( var j = 0, l = context.length; j < l; j++ ) { - jQuery.event.add( context[j], "live." + liveConvert( type, selector ), - { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); - } - - } else { - // unbind live handler - context.unbind( "live." + liveConvert( type, selector ), fn ); - } - } - - return this; - }; -}); - -function liveHandler( event ) { - var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, - elems = [], - selectors = [], - events = jQuery._data( this, "events" ); - - // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) - if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { - return; - } - - if ( event.namespace ) { - namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); - } - - event.liveFired = this; - - var live = events.live.slice(0); - - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { - selectors.push( handleObj.selector ); - - } else { - live.splice( j--, 1 ); - } - } - - match = jQuery( event.target ).closest( selectors, event.currentTarget ); - - for ( i = 0, l = match.length; i < l; i++ ) { - close = match[i]; - - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { - elem = close.elem; - related = null; - - // Those two events require additional checking - if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { - event.type = handleObj.preType; - related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; - - // Make sure not to accidentally match a child element with the same selector - if ( related && jQuery.contains( elem, related ) ) { - related = elem; - } - } - - if ( !related || related !== elem ) { - elems.push({ elem: elem, handleObj: handleObj, level: close.level }); - } - } - } - } - - for ( i = 0, l = elems.length; i < l; i++ ) { - match = elems[i]; - - if ( maxLevel && match.level > maxLevel ) { - break; - } - - event.currentTarget = match.elem; - event.data = match.handleObj.data; - event.handleObj = match.handleObj; - - ret = match.handleObj.origHandler.apply( match.elem, arguments ); - - if ( ret === false || event.isPropagationStopped() ) { - maxLevel = match.level; - - if ( ret === false ) { - stop = false; - } - if ( event.isImmediatePropagationStopped() ) { - break; - } - } - } - - return stop; -} - -function liveConvert( type, selector ) { - return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); -} - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.bind( name, data, fn ) : - this.trigger( name ); - }; - - if ( jQuery.attrFn ) { - jQuery.attrFn[ name ] = true; - } -}); - - - -/*! - * Sizzle CSS Selector Engine - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true, - rBackslash = /\\/g, - rNonWord = /\W/; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function( selector, context, results, seed ) { - results = results || []; - context = context || document; - - var origContext = context; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var m, set, checkSet, extra, ret, cur, pop, i, - prune = true, - contextXML = Sizzle.isXML( context ), - parts = [], - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - do { - chunker.exec( "" ); - m = chunker.exec( soFar ); - - if ( m ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - } while ( m ); - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context ); - - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set ); - } - } - - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - - ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? - Sizzle.filter( ret.expr, ret.set )[0] : - ret.set[0]; - } - - if ( context ) { - ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - - set = ret.expr ? - Sizzle.filter( ret.expr, ret.set ) : - ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray( set ); - - } else { - prune = false; - } - - while ( parts.length ) { - cur = parts.pop(); - pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - - } else if ( context && context.nodeType === 1 ) { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - - } else { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function( results ) { - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - } - - return results; -}; - -Sizzle.matches = function( expr, set ) { - return Sizzle( expr, null, null, set ); -}; - -Sizzle.matchesSelector = function( node, expr ) { - return Sizzle( expr, null, null, [node] ).length > 0; -}; - -Sizzle.find = function( expr, context, isXML ) { - var set; - - if ( !expr ) { - return []; - } - - for ( var i = 0, l = Expr.order.length; i < l; i++ ) { - var match, - type = Expr.order[i]; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - var left = match[1]; - match.splice( 1, 1 ); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace( rBackslash, "" ); - set = Expr.find[ type ]( match, context, isXML ); - - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = typeof context.getElementsByTagName !== "undefined" ? - context.getElementsByTagName( "*" ) : - []; - } - - return { set: set, expr: expr }; -}; - -Sizzle.filter = function( expr, set, inplace, not ) { - var match, anyFound, - old = expr, - result = [], - curLoop = set, - isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); - - while ( expr && set.length ) { - for ( var type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - var found, item, - filter = Expr.filter[ type ], - left = match[1]; - - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( var i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - var pass = not ^ !!found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - - } else { - curLoop[i] = false; - } - - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw "Syntax error, unrecognized expression: " + msg; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); - }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - - "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - - "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } - }, - - find: { - ID: function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }, - - NAME: function( match, context ) { - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], - results = context.getElementsByName( match[1] ); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - - TAG: function( match, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( match[1] ); - } - } - }, - preFilter: { - CLASS: function( match, curLoop, inplace, result, not, isXML ) { - match = " " + match[1].replace( rBackslash, "" ) + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - - ID: function( match ) { - return match[1].replace( rBackslash, "" ); - }, - - TAG: function( match, curLoop ) { - return match[1].replace( rBackslash, "" ).toLowerCase(); - }, - - CHILD: function( match ) { - if ( match[1] === "nth" ) { - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - match[2] = match[2].replace(/^\+|\s*/g, ''); - - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - - ATTR: function( match, curLoop, inplace, result, not, isXML ) { - var name = match[1] = match[1].replace( rBackslash, "" ); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - // Handle if an un-quoted value was used - match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - - PSEUDO: function( match, curLoop, inplace, result, not ) { - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - - if ( !inplace ) { - result.push.apply( result, ret ); - } - - return false; - } - - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - - POS: function( match ) { - match.unshift( true ); - - return match; - } - }, - - filters: { - enabled: function( elem ) { - return elem.disabled === false && elem.type !== "hidden"; - }, - - disabled: function( elem ) { - return elem.disabled === true; - }, - - checked: function( elem ) { - return elem.checked === true; - }, - - selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - parent: function( elem ) { - return !!elem.firstChild; - }, - - empty: function( elem ) { - return !elem.firstChild; - }, - - has: function( elem, i, match ) { - return !!Sizzle( match[3], elem ).length; - }, - - header: function( elem ) { - return (/h\d/i).test( elem.nodeName ); - }, - - text: function( elem ) { - var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); - }, - - radio: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; - }, - - checkbox: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; - }, - - file: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; - }, - - password: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; - }, - - submit: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "submit" === elem.type; - }, - - image: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; - }, - - reset: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "reset" === elem.type; - }, - - button: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && "button" === elem.type || name === "button"; - }, - - input: function( elem ) { - return (/input|select|textarea|button/i).test( elem.nodeName ); - }, - - focus: function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - setFilters: { - first: function( elem, i ) { - return i === 0; - }, - - last: function( elem, i, match, array ) { - return i === array.length - 1; - }, - - even: function( elem, i ) { - return i % 2 === 0; - }, - - odd: function( elem, i ) { - return i % 2 === 1; - }, - - lt: function( elem, i, match ) { - return i < match[3] - 0; - }, - - gt: function( elem, i, match ) { - return i > match[3] - 0; - }, - - nth: function( elem, i, match ) { - return match[3] - 0 === i; - }, - - eq: function( elem, i, match ) { - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function( elem, match, i, array ) { - var name = match[1], - filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; - - } else if ( name === "not" ) { - var not = match[3]; - - for ( var j = 0, l = not.length; j < l; j++ ) { - if ( not[j] === elem ) { - return false; - } - } - - return true; - - } else { - Sizzle.error( name ); - } - }, - - CHILD: function( elem, match ) { - var type = match[1], - node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - - case "nth": - var first = match[2], - last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - var doneName = match[0], - parent = elem.parentNode; - - if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { - var count = 0; - - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - - parent.sizcache = doneName; - } - - var diff = elem.nodeIndex - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - - ID: function( elem, match ) { - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - - TAG: function( elem, match ) { - return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; - }, - - CLASS: function( elem, match ) { - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - - ATTR: function( elem, match ) { - var name = match[1], - result = Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - - POS: function( elem, match, i, array ) { - var name = match[2], - filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS, - fescape = function(all, num){ - return "\\" + (num - 0 + 1); - }; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); -} - -var makeArray = function( array, results ) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -// Also verifies that the returned array holds DOM nodes -// (which is not the case in the Blackberry browser) -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; - -// Provide a fallback method if it does not work -} catch( e ) { - makeArray = function( array, results ) { - var i = 0, - ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - - } else { - if ( typeof array.length === "number" ) { - for ( var l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - - } else { - for ( ; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder, siblingCheck; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - return a.compareDocumentPosition ? -1 : 1; - } - - return a.compareDocumentPosition(b) & 4 ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} - -// Utility function for retreiving the text value of an array of DOM nodes -Sizzle.getText = function( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += Sizzle.getText( elem.childNodes ); - } - } - - return ret; -}; - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date()).getTime(), - root = document.documentElement; - - form.innerHTML = "<a name='" + id + "'/>"; - - // Inject it into the root element, check its status, and remove it quickly - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - - return m ? - m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? - [m] : - undefined : - []; - } - }; - - Expr.filter.ID = function( elem, match ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - - // release memory in IE - root = form = null; -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function( match, context ) { - var results = context.getElementsByTagName( match[1] ); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = "<a href='#'></a>"; - - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - - Expr.attrHandle.href = function( elem ) { - return elem.getAttribute( "href", 2 ); - }; - } - - // release memory in IE - div = null; -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, - div = document.createElement("div"), - id = "__sizzle__"; - - div.innerHTML = "<p class='TEST'></p>"; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function( query, context, extra, seed ) { - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && !Sizzle.isXML(context) ) { - // See if we find a selector to speed up - var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); - - if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { - // Speed-up: Sizzle("TAG") - if ( match[1] ) { - return makeArray( context.getElementsByTagName( query ), extra ); - - // Speed-up: Sizzle(".CLASS") - } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { - return makeArray( context.getElementsByClassName( match[2] ), extra ); - } - } - - if ( context.nodeType === 9 ) { - // Speed-up: Sizzle("body") - // The body element only exists once, optimize finding it - if ( query === "body" && context.body ) { - return makeArray( [ context.body ], extra ); - - // Speed-up: Sizzle("#ID") - } else if ( match && match[3] ) { - var elem = context.getElementById( match[3] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id === match[3] ) { - return makeArray( [ elem ], extra ); - } - - } else { - return makeArray( [], extra ); - } - } - - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(qsaError) {} - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var oldContext = context, - old = context.getAttribute( "id" ), - nid = old || id, - hasParent = context.parentNode, - relativeHierarchySelector = /^\s*[+~]/.test( query ); - - if ( !old ) { - context.setAttribute( "id", nid ); - } else { - nid = nid.replace( /'/g, "\\$&" ); - } - if ( relativeHierarchySelector && hasParent ) { - context = context.parentNode; - } - - try { - if ( !relativeHierarchySelector || hasParent ) { - return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); - } - - } catch(pseudoError) { - } finally { - if ( !old ) { - oldContext.removeAttribute( "id" ); - } - } - } - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - // release memory in IE - div = null; - })(); -} - -(function(){ - var html = document.documentElement, - matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; - - if ( matches ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9 fails this) - var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), - pseudoWorks = false; - - try { - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( document.documentElement, "[test!='']:sizzle" ); - - } catch( pseudoError ) { - pseudoWorks = true; - } - - Sizzle.matchesSelector = function( node, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); - - if ( !Sizzle.isXML( node ) ) { - try { - if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { - var ret = matches.call( node, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || !disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9, so check for that - node.document && node.document.nodeType !== 11 ) { - return ret; - } - } - } catch(e) {} - } - - return Sizzle(expr, null, null, [node]).length > 0; - }; - } -})(); - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "<div class='test e'></div><div class='test'></div>"; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByClassName actually exists - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByClassName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function( match, context, isXML ) { - if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { - return context.getElementsByClassName(match[1]); - } - }; - - // release memory in IE - div = null; -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem.sizcache = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem.sizcache = doneName; - elem.sizset = i; - } - - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -if ( document.documentElement.contains ) { - Sizzle.contains = function( a, b ) { - return a !== b && (a.contains ? a.contains(b) : true); - }; - -} else if ( document.documentElement.compareDocumentPosition ) { - Sizzle.contains = function( a, b ) { - return !!(a.compareDocumentPosition(b) & 16); - }; - -} else { - Sizzle.contains = function() { - return false; - }; -} - -Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function( selector, context ) { - var match, - tmpSet = [], - later = "", - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.filters; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})(); - - -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - isSimple = /^.[^:#\[\.,]*$/, - slice = Array.prototype.slice, - POS = jQuery.expr.match.POS, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var self = this, - i, l; - - if ( typeof selector !== "string" ) { - return jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - var ret = this.pushStack( "", "find", selector ), - length, n, r; - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( typeof selector === "string" ? - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var ret = [], i, l, cur = this[0]; - - // Array - if ( jQuery.isArray( selectors ) ) { - var match, selector, - matches = {}, - level = 1; - - if ( cur && selectors.length ) { - for ( i = 0, l = selectors.length; i < l; i++ ) { - selector = selectors[i]; - - if ( !matches[ selector ] ) { - matches[ selector ] = POS.test( selector ) ? - jQuery( selector, context || this.context ) : - selector; - } - } - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( selector in matches ) { - match = matches[ selector ]; - - if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { - ret.push({ selector: selector, elem: cur, level: level }); - } - } - - cur = cur.parentNode; - level++; - } - } - - return ret; - } - - // String - var pos = POS.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( i = 0, l = this.length; i < l; i++ ) { - cur = this[i]; - - while ( cur ) { - if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - - } else { - cur = cur.parentNode; - if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { - break; - } - } - } - } - - ret = ret.length > 1 ? jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( elem.parentNode.firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ), - // The variable 'args' was introduced in - // https://github.com/jquery/jquery/commit/52a0238 - // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. - // http://code.google.com/p/v8/issues/detail?id=1050 - args = slice.call(arguments); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, args.join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return (elem === qualifier) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return (jQuery.inArray( elem, qualifier ) >= 0) === keep; - }); -} - - - - -var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, - rtagName = /<([\w:]+)/, - rtbody = /<tbody/i, - rhtml = /<|&#?\w+;/, - rnocache = /<(?:script|object|embed|option|style)/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /\/(java|ecma)script/i, - rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/, - wrapMap = { - option: [ 1, "<select multiple='multiple'>", "</select>" ], - legend: [ 1, "<fieldset>", "</fieldset>" ], - thead: [ 1, "<table>", "</table>" ], - tr: [ 2, "<table><tbody>", "</tbody></table>" ], - td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], - col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], - area: [ 1, "<map>", "</map>" ], - _default: [ 0, "", "" ] - }; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize <link> and <script> tags normally -if ( !jQuery.support.htmlSerialize ) { - wrapMap._default = [ 1, "div<div>", "</div>" ]; -} - -jQuery.fn.extend({ - text: function( text ) { - if ( jQuery.isFunction(text) ) { - return this.each(function(i) { - var self = jQuery( this ); - - self.text( text.call(this, i, self.text()) ); - }); - } - - if ( typeof text !== "object" && text !== undefined ) { - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - } - - return jQuery.text( this ); - }, - - wrapAll: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function(i) { - jQuery(this).wrapAll( html.call(this, i) ); - }); - } - - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); - - if ( this[0].parentNode ) { - wrap.insertBefore( this[0] ); - } - - wrap.map(function() { - var elem = this; - - while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { - elem = elem.firstChild; - } - - return elem; - }).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function(i) { - jQuery(this).wrapInner( html.call(this, i) ); - }); - } - - return this.each(function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - }); - }, - - wrap: function( html ) { - return this.each(function() { - jQuery( this ).wrapAll( html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - }, - - append: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 ) { - this.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 ) { - this.insertBefore( elem, this.firstChild ); - } - }); - }, - - before: function() { - if ( this[0] && this[0].parentNode ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this ); - }); - } else if ( arguments.length ) { - var set = jQuery(arguments[0]); - set.push.apply( set, this.toArray() ); - return this.pushStack( set, "before", arguments ); - } - }, - - after: function() { - if ( this[0] && this[0].parentNode ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - } else if ( arguments.length ) { - var set = this.pushStack( this, "after", arguments ); - set.push.apply( set, jQuery(arguments[0]).toArray() ); - return set; - } - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - jQuery.cleanData( [ elem ] ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - } - } - - return this; - }, - - empty: function() { - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - } - - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function () { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - if ( value === undefined ) { - return this[0] && this[0].nodeType === 1 ? - this[0].innerHTML.replace(rinlinejQuery, "") : - null; - - // See if we can take a shortcut and just use innerHTML - } else if ( typeof value === "string" && !rnocache.test( value ) && - (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && - !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { - - value = value.replace(rxhtmlTag, "<$1></$2>"); - - try { - for ( var i = 0, l = this.length; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - if ( this[i].nodeType === 1 ) { - jQuery.cleanData( this[i].getElementsByTagName("*") ); - this[i].innerHTML = value; - } - } - - // If using innerHTML throws an exception, use the fallback method - } catch(e) { - this.empty().append( value ); - } - - } else if ( jQuery.isFunction( value ) ) { - this.each(function(i){ - var self = jQuery( this ); - - self.html( value.call(this, i, self.html()) ); - }); - - } else { - this.empty().append( value ); - } - - return this; - }, - - replaceWith: function( value ) { - if ( this[0] && this[0].parentNode ) { - // Make sure that the elements are removed from the DOM before they are inserted - // this can help fix replacing a parent with child elements - if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this), old = self.html(); - self.replaceWith( value.call( this, i, old ) ); - }); - } - - if ( typeof value !== "string" ) { - value = jQuery( value ).detach(); - } - - return this.each(function() { - var next = this.nextSibling, - parent = this.parentNode; - - jQuery( this ).remove(); - - if ( next ) { - jQuery(next).before( value ); - } else { - jQuery(parent).append( value ); - } - }); - } else { - return this.length ? - this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : - this; - } - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, table, callback ) { - var results, first, fragment, parent, - value = args[0], - scripts = []; - - // We can't cloneNode fragments that contain checked, in WebKit - if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) { - return this.each(function() { - jQuery(this).domManip( args, table, callback, true ); - }); - } - - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - args[0] = value.call(this, i, table ? self.html() : undefined); - self.domManip( args, table, callback ); - }); - } - - if ( this[0] ) { - parent = value && value.parentNode; - - // If we're in a fragment, just use that instead of building a new one - if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) { - results = { fragment: parent }; - - } else { - results = jQuery.buildFragment( args, this, scripts ); - } - - fragment = results.fragment; - - if ( fragment.childNodes.length === 1 ) { - first = fragment = fragment.firstChild; - } else { - first = fragment.firstChild; - } - - if ( first ) { - table = table && jQuery.nodeName( first, "tr" ); - - for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) { - callback.call( - table ? - root(this[i], first) : - this[i], - // Make sure that we do not leak memory by inadvertently discarding - // the original fragment (which might have attached data) instead of - // using it; in addition, use the original fragment object for the last - // item instead of first because it can end up being emptied incorrectly - // in certain situations (Bug #8070). - // Fragments from the fragment cache must always be cloned and never used - // in place. - results.cacheable || (l > 1 && i < lastIndex) ? - jQuery.clone( fragment, true, true ) : - fragment - ); - } - } - - if ( scripts.length ) { - jQuery.each( scripts, evalScript ); - } - } - - return this; - } -}); - -function root( elem, cur ) { - return jQuery.nodeName(elem, "table") ? - (elem.getElementsByTagName("tbody")[0] || - elem.appendChild(elem.ownerDocument.createElement("tbody"))) : - elem; -} - -function cloneCopyEvent( src, dest ) { - - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } - - var internalKey = jQuery.expando, - oldData = jQuery.data( src ), - curData = jQuery.data( dest, oldData ); - - // Switch to use the internal data object, if it exists, for the next - // stage of data copying - if ( (oldData = oldData[ internalKey ]) ) { - var events = oldData.events; - curData = curData[ internalKey ] = jQuery.extend({}, oldData); - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( var type in events ) { - for ( var i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); - } - } - } - } -} - -function cloneFixAttributes( src, dest ) { - var nodeName; - - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } - - // clearAttributes removes the attributes, which we don't want, - // but also removes the attachEvent events, which we *do* want - if ( dest.clearAttributes ) { - dest.clearAttributes(); - } - - // mergeAttributes, in contrast, only merges back on the - // original attributes, not the events - if ( dest.mergeAttributes ) { - dest.mergeAttributes( src ); - } - - nodeName = dest.nodeName.toLowerCase(); - - // IE6-8 fail to clone children inside object elements that use - // the proprietary classid attribute value (rather than the type - // attribute) to identify the type of content to display - if ( nodeName === "object" ) { - dest.outerHTML = src.outerHTML; - - } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - if ( src.checked ) { - dest.defaultChecked = dest.checked = src.checked; - } - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } - - // Event data gets referenced instead of copied if the expando - // gets copied too - dest.removeAttribute( jQuery.expando ); -} - -jQuery.buildFragment = function( args, nodes, scripts ) { - var fragment, cacheable, cacheresults, doc; - - // nodes may contain either an explicit document object, - // a jQuery collection or context object. - // If nodes[0] contains a valid object to assign to doc - if ( nodes && nodes[0] ) { - doc = nodes[0].ownerDocument || nodes[0]; - } - - // Ensure that an attr object doesn't incorrectly stand in as a document object - // Chrome and Firefox seem to allow this to occur and will throw exception - // Fixes #8950 - if ( !doc.createDocumentFragment ) { - doc = document; - } - - // Only cache "small" (1/2 KB) HTML strings that are associated with the main document - // Cloning options loses the selected state, so don't cache them - // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment - // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache - if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && - args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { - - cacheable = true; - - cacheresults = jQuery.fragments[ args[0] ]; - if ( cacheresults && cacheresults !== 1 ) { - fragment = cacheresults; - } - } - - if ( !fragment ) { - fragment = doc.createDocumentFragment(); - jQuery.clean( args, doc, fragment, scripts ); - } - - if ( cacheable ) { - jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1; - } - - return { fragment: fragment, cacheable: cacheable }; -}; - -jQuery.fragments = {}; - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var ret = [], - insert = jQuery( selector ), - parent = this.length === 1 && this[0].parentNode; - - if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { - insert[ original ]( this[0] ); - return this; - - } else { - for ( var i = 0, l = insert.length; i < l; i++ ) { - var elems = (i > 0 ? this.clone(true) : this).get(); - jQuery( insert[i] )[ original ]( elems ); - ret = ret.concat( elems ); - } - - return this.pushStack( ret, name, insert.selector ); - } - }; -}); - -function getAll( elem ) { - if ( "getElementsByTagName" in elem ) { - return elem.getElementsByTagName( "*" ); - - } else if ( "querySelectorAll" in elem ) { - return elem.querySelectorAll( "*" ); - - } else { - return []; - } -} - -// Used in clean, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( elem.type === "checkbox" || elem.type === "radio" ) { - elem.defaultChecked = elem.checked; - } -} -// Finds all inputs and passes them to fixDefaultChecked -function findInputs( elem ) { - if ( jQuery.nodeName( elem, "input" ) ) { - fixDefaultChecked( elem ); - } else if ( "getElementsByTagName" in elem ) { - jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var clone = elem.cloneNode(true), - srcElements, - destElements, - i; - - if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { - // IE copies events bound via attachEvent when using cloneNode. - // Calling detachEvent on the clone will also remove the events - // from the original. In order to get around this, we use some - // proprietary methods to clear the events. Thanks to MooTools - // guys for this hotness. - - cloneFixAttributes( elem, clone ); - - // Using Sizzle here is crazy slow, so we use getElementsByTagName - // instead - srcElements = getAll( elem ); - destElements = getAll( clone ); - - // Weird iteration because IE will replace the length property - // with an element if you are cloning the body and one of the - // elements on the page has a name or id of "length" - for ( i = 0; srcElements[i]; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - cloneFixAttributes( srcElements[i], destElements[i] ); - } - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - cloneCopyEvent( elem, clone ); - - if ( deepDataAndEvents ) { - srcElements = getAll( elem ); - destElements = getAll( clone ); - - for ( i = 0; srcElements[i]; ++i ) { - cloneCopyEvent( srcElements[i], destElements[i] ); - } - } - } - - srcElements = destElements = null; - - // Return the cloned set - return clone; - }, - - clean: function( elems, context, fragment, scripts ) { - var checkScriptType; - - context = context || document; - - // !context.createElement fails in IE with an error but returns typeof 'object' - if ( typeof context.createElement === "undefined" ) { - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - } - - var ret = [], j; - - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - if ( typeof elem === "number" ) { - elem += ""; - } - - if ( !elem ) { - continue; - } - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - if ( !rhtml.test( elem ) ) { - elem = context.createTextNode( elem ); - } else { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(rxhtmlTag, "<$1></$2>"); - - // Trim whitespace, otherwise indexOf won't work as expected - var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), - wrap = wrapMap[ tag ] || wrapMap._default, - depth = wrap[0], - div = context.createElement("div"); - - // Go to html and back, then peel off extra wrappers - div.innerHTML = wrap[1] + elem + wrap[2]; - - // Move to the right depth - while ( depth-- ) { - div = div.lastChild; - } - - // Remove IE's autoinserted <tbody> from table fragments - if ( !jQuery.support.tbody ) { - - // String was a <table>, *may* have spurious <tbody> - var hasBody = rtbody.test(elem), - tbody = tag === "table" && !hasBody ? - div.firstChild && div.firstChild.childNodes : - - // String was a bare <thead> or <tfoot> - wrap[1] === "<table>" && !hasBody ? - div.childNodes : - []; - - for ( j = tbody.length - 1; j >= 0 ; --j ) { - if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { - tbody[ j ].parentNode.removeChild( tbody[ j ] ); - } - } - } - - // IE completely kills leading whitespace when innerHTML is used - if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); - } - - elem = div.childNodes; - } - } - - // Resets defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - var len; - if ( !jQuery.support.appendChecked ) { - if ( elem[0] && typeof (len = elem.length) === "number" ) { - for ( j = 0; j < len; j++ ) { - findInputs( elem[j] ); - } - } else { - findInputs( elem ); - } - } - - if ( elem.nodeType ) { - ret.push( elem ); - } else { - ret = jQuery.merge( ret, elem ); - } - } - - if ( fragment ) { - checkScriptType = function( elem ) { - return !elem.type || rscriptType.test( elem.type ); - }; - for ( i = 0; ret[i]; i++ ) { - if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { - scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); - - } else { - if ( ret[i].nodeType === 1 ) { - var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType ); - - ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); - } - fragment.appendChild( ret[i] ); - } - } - } - - return ret; - }, - - cleanData: function( elems ) { - var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, - deleteExpando = jQuery.support.deleteExpando; - - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { - continue; - } - - id = elem[ jQuery.expando ]; - - if ( id ) { - data = cache[ id ] && cache[ id ][ internalKey ]; - - if ( data && data.events ) { - for ( var type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - - // Null the DOM reference to avoid IE6/7/8 leak (#7054) - if ( data.handle ) { - data.handle.elem = null; - } - } - - if ( deleteExpando ) { - delete elem[ jQuery.expando ]; - - } else if ( elem.removeAttribute ) { - elem.removeAttribute( jQuery.expando ); - } - - delete cache[ id ]; - } - } - } -}); - -function evalScript( i, elem ) { - if ( elem.src ) { - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - } else { - jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } -} - - - - -var ralpha = /alpha\([^)]*\)/i, - ropacity = /opacity=([^)]*)/, - // fixed for IE9, see #8346 - rupper = /([A-Z]|^ms)/g, - rnumpx = /^-?\d+(?:px)?$/i, - rnum = /^-?\d/, - rrelNum = /^([\-+])=([\-+.\de]+)/, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssWidth = [ "Left", "Right" ], - cssHeight = [ "Top", "Bottom" ], - curCSS, - - getComputedStyle, - currentStyle; - -jQuery.fn.css = function( name, value ) { - // Setting 'undefined' is a no-op - if ( arguments.length === 2 && value === undefined ) { - return this; - } - - return jQuery.access( this, name, value, true, function( elem, name, value ) { - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }); -}; - -jQuery.extend({ - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity", "opacity" ); - return ret === "" ? "1" : ret; - - } else { - return elem.style.opacity; - } - } - } - }, - - // Exclude the following css properties to add px - cssNumber: { - "fillOpacity": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - // normalize float css property - "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, origName = jQuery.camelCase( name ), - style = elem.style, hooks = jQuery.cssHooks[ origName ]; - - name = jQuery.cssProps[ origName ] || origName; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) ); - // Fixes bug #9237 - type = "number"; - } - - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { - return; - } - - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) { - // Wrapped to prevent IE from throwing errors when 'invalid' values are provided - // Fixes bug #5509 - try { - style[ name ] = value; - } catch(e) {} - } - - } else { - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra ) { - var ret, hooks; - - // Make sure that we're working with the right name - name = jQuery.camelCase( name ); - hooks = jQuery.cssHooks[ name ]; - name = jQuery.cssProps[ name ] || name; - - // cssFloat needs a special treatment - if ( name === "cssFloat" ) { - name = "float"; - } - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { - return ret; - - // Otherwise, if a way to get the computed value exists, use that - } else if ( curCSS ) { - return curCSS( elem, name ); - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - } -}); - -// DEPRECATED, Use jQuery.css() instead -jQuery.curCSS = jQuery.css; - -jQuery.each(["height", "width"], function( i, name ) { - jQuery.cssHooks[ name ] = { - get: function( elem, computed, extra ) { - var val; - - if ( computed ) { - if ( elem.offsetWidth !== 0 ) { - return getWH( elem, name, extra ); - } else { - jQuery.swap( elem, cssShow, function() { - val = getWH( elem, name, extra ); - }); - } - - return val; - } - }, - - set: function( elem, value ) { - if ( rnumpx.test( value ) ) { - // ignore negative width and height values #1599 - value = parseFloat( value ); - - if ( value >= 0 ) { - return value + "px"; - } - - } else { - return value; - } - } - }; -}); - -if ( !jQuery.support.opacity ) { - jQuery.cssHooks.opacity = { - get: function( elem, computed ) { - // IE uses filters for opacity - return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? - ( parseFloat( RegExp.$1 ) / 100 ) + "" : - computed ? "1" : ""; - }, - - set: function( elem, value ) { - var style = elem.style, - currentStyle = elem.currentStyle, - opacity = jQuery.isNaN( value ) ? "" : "alpha(opacity=" + value * 100 + ")", - filter = currentStyle && currentStyle.filter || style.filter || ""; - - // IE has trouble with opacity if it does not have layout - // Force it by setting the zoom level - style.zoom = 1; - - // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 - if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) { - - // Setting style.filter to null, "" & " " still leave "filter:" in the cssText - // if "filter:" is present at all, clearType is disabled, we want to avoid this - // style.removeAttribute is IE Only, but so apparently is this code path... - style.removeAttribute( "filter" ); - - // if there there is no filter style applied in a css rule, we are done - if ( currentStyle && !currentStyle.filter ) { - return; - } - } - - // otherwise, set new filter values - style.filter = ralpha.test( filter ) ? - filter.replace( ralpha, opacity ) : - filter + " " + opacity; - } - }; -} - -jQuery(function() { - // This hook cannot be added until DOM ready because the support test - // for it is not run until after DOM ready - if ( !jQuery.support.reliableMarginRight ) { - jQuery.cssHooks.marginRight = { - get: function( elem, computed ) { - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - // Work around by temporarily setting element display to inline-block - var ret; - jQuery.swap( elem, { "display": "inline-block" }, function() { - if ( computed ) { - ret = curCSS( elem, "margin-right", "marginRight" ); - } else { - ret = elem.style.marginRight; - } - }); - return ret; - } - }; - } -}); - -if ( document.defaultView && document.defaultView.getComputedStyle ) { - getComputedStyle = function( elem, name ) { - var ret, defaultView, computedStyle; - - name = name.replace( rupper, "-$1" ).toLowerCase(); - - if ( !(defaultView = elem.ownerDocument.defaultView) ) { - return undefined; - } - - if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) { - ret = computedStyle.getPropertyValue( name ); - if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { - ret = jQuery.style( elem, name ); - } - } - - return ret; - }; -} - -if ( document.documentElement.currentStyle ) { - currentStyle = function( elem, name ) { - var left, - ret = elem.currentStyle && elem.currentStyle[ name ], - rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ], - style = elem.style; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !rnumpx.test( ret ) && rnum.test( ret ) ) { - // Remember the original values - left = style.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - elem.runtimeStyle.left = elem.currentStyle.left; - } - style.left = name === "fontSize" ? "1em" : (ret || 0); - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - elem.runtimeStyle.left = rsLeft; - } - } - - return ret === "" ? "auto" : ret; - }; -} - -curCSS = getComputedStyle || currentStyle; - -function getWH( elem, name, extra ) { - - // Start with offset property - var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - which = name === "width" ? cssWidth : cssHeight; - - if ( val > 0 ) { - if ( extra !== "border" ) { - jQuery.each( which, function() { - if ( !extra ) { - val -= parseFloat( jQuery.css( elem, "padding" + this ) ) || 0; - } - if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + this ) ) || 0; - } else { - val -= parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0; - } - }); - } - - return val + "px"; - } - - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name, name ); - if ( val < 0 || val == null ) { - val = elem.style[ name ] || 0; - } - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - - // Add padding, border, margin - if ( extra ) { - jQuery.each( which, function() { - val += parseFloat( jQuery.css( elem, "padding" + this ) ) || 0; - if ( extra !== "padding" ) { - val += parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0; - } - if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + this ) ) || 0; - } - }); - } - - return val + "px"; -} - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.hidden = function( elem ) { - var width = elem.offsetWidth, - height = elem.offsetHeight; - - return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none"); - }; - - jQuery.expr.filters.visible = function( elem ) { - return !jQuery.expr.filters.hidden( elem ); - }; -} - - - - -var r20 = /%20/g, - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rhash = /#.*$/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL - rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - rquery = /\?/, - rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, - rselectTextarea = /^(?:select|textarea)/i, - rspacesAjax = /\s+/, - rts = /([?&])_=[^&]*/, - rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/, - - // Keep a copy of the old load method - _load = jQuery.fn.load, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Document location - ajaxLocation, - - // Document location segments - ajaxLocParts, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = ["*/"] + ["*"]; - -// #8138, IE may throw an exception when accessing -// a field from window.location if document.domain has been set -try { - ajaxLocation = location.href; -} catch( e ) { - // Use the href attribute of an A element - // since IE will modify it given document.location - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} - -// Segment location into parts -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - if ( jQuery.isFunction( func ) ) { - var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), - i = 0, - length = dataTypes.length, - dataType, - list, - placeBefore; - - // For each dataType in the dataTypeExpression - for(; i < length; i++ ) { - dataType = dataTypes[ i ]; - // We control if we're asked to add before - // any existing element - placeBefore = /^\+/.test( dataType ); - if ( placeBefore ) { - dataType = dataType.substr( 1 ) || "*"; - } - list = structure[ dataType ] = structure[ dataType ] || []; - // then we add to the structure accordingly - list[ placeBefore ? "unshift" : "push" ]( func ); - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, - dataType /* internal */, inspected /* internal */ ) { - - dataType = dataType || options.dataTypes[ 0 ]; - inspected = inspected || {}; - - inspected[ dataType ] = true; - - var list = structure[ dataType ], - i = 0, - length = list ? list.length : 0, - executeOnly = ( structure === prefilters ), - selection; - - for(; i < length && ( executeOnly || !selection ); i++ ) { - selection = list[ i ]( options, originalOptions, jqXHR ); - // If we got redirected to another dataType - // we try there if executing only and not done already - if ( typeof selection === "string" ) { - if ( !executeOnly || inspected[ selection ] ) { - selection = undefined; - } else { - options.dataTypes.unshift( selection ); - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, selection, inspected ); - } - } - } - // If we're only executing or nothing was selected - // we try the catchall dataType if not done already - if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, "*", inspected ); - } - // unnecessary when only executing (prefilters) - // but it'll be ignored by the caller in that case - return selection; -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - for( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } -} - -jQuery.fn.extend({ - load: function( url, params, callback ) { - if ( typeof url !== "string" && _load ) { - return _load.apply( this, arguments ); - - // Don't do a request if no elements are being requested - } else if ( !this.length ) { - return this; - } - - var off = url.indexOf( " " ); - if ( off >= 0 ) { - var selector = url.slice( off, url.length ); - url = url.slice( 0, off ); - } - - // Default to a GET request - var type = "GET"; - - // If the second parameter was provided - if ( params ) { - // If it's a function - if ( jQuery.isFunction( params ) ) { - // We assume that it's the callback - callback = params; - params = undefined; - - // Otherwise, build a param string - } else if ( typeof params === "object" ) { - params = jQuery.param( params, jQuery.ajaxSettings.traditional ); - type = "POST"; - } - } - - var self = this; - - // Request the remote document - jQuery.ajax({ - url: url, - type: type, - dataType: "html", - data: params, - // Complete callback (responseText is used internally) - complete: function( jqXHR, status, responseText ) { - // Store the response as specified by the jqXHR object - responseText = jqXHR.responseText; - // If successful, inject the HTML into all the matched elements - if ( jqXHR.isResolved() ) { - // #4825: Get the actual response in case - // a dataFilter is present in ajaxSettings - jqXHR.done(function( r ) { - responseText = r; - }); - // See if a selector was specified - self.html( selector ? - // Create a dummy div to hold the results - jQuery("<div>") - // inject the contents of the document in, removing the scripts - // to avoid any 'Permission Denied' errors in IE - .append(responseText.replace(rscript, "")) - - // Locate the specified elements - .find(selector) : - - // If not, just inject the full result - responseText ); - } - - if ( callback ) { - self.each( callback, [ responseText, status, jqXHR ] ); - } - } - }); - - return this; - }, - - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - - serializeArray: function() { - return this.map(function(){ - return this.elements ? jQuery.makeArray( this.elements ) : this; - }) - .filter(function(){ - return this.name && !this.disabled && - ( this.checked || rselectTextarea.test( this.nodeName ) || - rinput.test( this.type ) ); - }) - .map(function( i, elem ){ - var val = jQuery( this ).val(); - - return val == null ? - null : - jQuery.isArray( val ) ? - jQuery.map( val, function( val, i ){ - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }) : - { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }).get(); - } -}); - -// Attach a bunch of functions for handling common AJAX events -jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ - jQuery.fn[ o ] = function( f ){ - return this.bind( o, f ); - }; -}); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - return jQuery.ajax({ - type: method, - url: url, - data: data, - success: callback, - dataType: type - }); - }; -}); - -jQuery.extend({ - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - if ( settings ) { - // Building a settings object - ajaxExtend( target, jQuery.ajaxSettings ); - } else { - // Extending ajaxSettings - settings = target; - target = jQuery.ajaxSettings; - } - ajaxExtend( target, settings ); - return target; - }, - - ajaxSettings: { - url: ajaxLocation, - isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), - global: true, - type: "GET", - contentType: "application/x-www-form-urlencoded", - processData: true, - async: true, - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - traditional: false, - headers: {}, - */ - - accepts: { - xml: "application/xml, text/xml", - html: "text/html", - text: "text/plain", - json: "application/json, text/javascript", - "*": allTypes - }, - - contents: { - xml: /xml/, - html: /html/, - json: /json/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText" - }, - - // List of data converters - // 1) key format is "source_type destination_type" (a single space in-between) - // 2) the catchall symbol "*" can be used for source_type - converters: { - - // Convert anything to text - "* text": window.String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": jQuery.parseJSON, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - context: true, - url: true - } - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - // Callbacks context - callbackContext = s.context || s, - // Context for global events - // It's the callbackContext if one was provided in the options - // and if it's a DOM node or a jQuery collection - globalEventContext = callbackContext !== s && - ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? - jQuery( callbackContext ) : jQuery.event, - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery._Deferred(), - // Status-dependent callbacks - statusCode = s.statusCode || {}, - // ifModified key - ifModifiedKey, - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - // Response headers - responseHeadersString, - responseHeaders, - // transport - transport, - // timeout handle - timeoutTimer, - // Cross-domain detection vars - parts, - // The jqXHR state - state = 0, - // To know if global events are to be dispatched - fireGlobals, - // Loop variable - i, - // Fake xhr - jqXHR = { - - readyState: 0, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( !state ) { - var lname = name.toLowerCase(); - name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Raw string - getAllResponseHeaders: function() { - return state === 2 ? responseHeadersString : null; - }, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( state === 2 ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; - } - } - match = responseHeaders[ key.toLowerCase() ]; - } - return match === undefined ? null : match; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( !state ) { - s.mimeType = type; - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - statusText = statusText || "abort"; - if ( transport ) { - transport.abort( statusText ); - } - done( 0, statusText ); - return this; - } - }; - - // Callback for when everything is done - // It is defined here because jslint complains if it is declared - // at the end of the function (which would be more logical and readable) - function done( status, nativeStatusText, responses, headers ) { - - // Called once - if ( state === 2 ) { - return; - } - - // State is "done" now - state = 2; - - // Clear timeout if it exists - if ( timeoutTimer ) { - clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - var isSuccess, - success, - error, - statusText = nativeStatusText, - response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, - lastModified, - etag; - - // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - - if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { - jQuery.lastModified[ ifModifiedKey ] = lastModified; - } - if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { - jQuery.etag[ ifModifiedKey ] = etag; - } - } - - // If not modified - if ( status === 304 ) { - - statusText = "notmodified"; - isSuccess = true; - - // If we have data - } else { - - try { - success = ajaxConvert( s, response ); - statusText = "success"; - isSuccess = true; - } catch(e) { - // We have a parsererror - statusText = "parsererror"; - error = e; - } - } - } else { - // We extract error from statusText - // then normalize statusText and status for non-aborts - error = statusText; - if( !statusText || status ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = "" + ( nativeStatusText || statusText ); - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - // Attach deferreds - deferred.promise( jqXHR ); - jqXHR.success = jqXHR.done; - jqXHR.error = jqXHR.fail; - jqXHR.complete = completeDeferred.done; - - // Status-dependent callbacks - jqXHR.statusCode = function( map ) { - if ( map ) { - var tmp; - if ( state < 2 ) { - for( tmp in map ) { - statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; - } - } else { - tmp = map[ jqXHR.status ]; - jqXHR.then( tmp, tmp ); - } - } - return this; - }; - - // Remove hash character (#7531: and string promotion) - // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) - // We also use the url parameter if available - s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); - - // Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); - - // Determine if a cross-domain request is in order - if ( s.crossDomain == null ) { - parts = rurl.exec( s.url.toLowerCase() ); - s.crossDomain = !!( parts && - ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || - ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != - ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) - ); - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefiler, stop there - if ( state === 2 ) { - return false; - } - - // We can fire global events as of now if asked to - fireGlobals = s.global; - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // If data is available, append data to url - if ( s.data ) { - s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Get ifModifiedKey before adding the anti-cache parameter - ifModifiedKey = s.url; - - // Add anti-cache in url if needed - if ( s.cache === false ) { - - var ts = jQuery.now(), - // try replacing _= if it is there - ret = s.url.replace( rts, "$1_=" + ts ); - - // if nothing was replaced, add timestamp to the end - s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - ifModifiedKey = ifModifiedKey || s.url; - if ( jQuery.lastModified[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); - } - if ( jQuery.etag[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); - } - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? - s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { - // Abort if not done already - jqXHR.abort(); - return false; - - } - - // Install callbacks on deferreds - for ( i in { success: 1, error: 1, complete: 1 } ) { - jqXHR[ i ]( s[ i ] ); - } - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout( function(){ - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - state = 1; - transport.send( requestHeaders, done ); - } catch (e) { - // Propagate exception as error if not done - if ( state < 2 ) { - done( -1, e ); - // Simply rethrow otherwise - } else { - jQuery.error( e ); - } - } - } - - return jqXHR; - }, - - // Serialize an array of form elements or a set of - // key/values into a query string - param: function( a, traditional ) { - var s = [], - add = function( key, value ) { - // If value is a function, invoke it and return its value - value = jQuery.isFunction( value ) ? value() : value; - s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); - }; - - // Set traditional to true for jQuery <= 1.3.2 behavior. - if ( traditional === undefined ) { - traditional = jQuery.ajaxSettings.traditional; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - }); - - } else { - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( var prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ).replace( r20, "+" ); - } -}); - -function buildParams( prefix, obj, traditional, add ) { - if ( jQuery.isArray( obj ) ) { - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - // If array item is non-scalar (array or object), encode its - // numeric index to resolve deserialization ambiguity issues. - // Note that rack (as of 1.0.0) can't currently deserialize - // nested arrays properly, and attempting to do so may cause - // a server error. Possible fixes are to modify rack's - // deserialization algorithm or to provide an option or flag - // to force array serialization to be shallow. - buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add ); - } - }); - - } else if ( !traditional && obj != null && typeof obj === "object" ) { - // Serialize object item. - for ( var name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - // Serialize scalar item. - add( prefix, obj ); - } -} - -// This is still on the jQuery object... for now -// Want to move this to jQuery.ajax some day -jQuery.extend({ - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {} - -}); - -/* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var contents = s.contents, - dataTypes = s.dataTypes, - responseFields = s.responseFields, - ct, - type, - finalDataType, - firstDataType; - - // Fill responseXXX fields - for( type in responseFields ) { - if ( type in responses ) { - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } - - // Remove auto dataType and get content-type in the process - while( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -// Chain conversions given the request and the original response -function ajaxConvert( s, response ) { - - // Apply the dataFilter if provided - if ( s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - var dataTypes = s.dataTypes, - converters = {}, - i, - key, - length = dataTypes.length, - tmp, - // Current and previous dataTypes - current = dataTypes[ 0 ], - prev, - // Conversion expression - conversion, - // Conversion function - conv, - // Conversion functions (transitive conversion) - conv1, - conv2; - - // For each dataType in the chain - for( i = 1; i < length; i++ ) { - - // Create converters map - // with lowercased keys - if ( i === 1 ) { - for( key in s.converters ) { - if( typeof key === "string" ) { - converters[ key.toLowerCase() ] = s.converters[ key ]; - } - } - } - - // Get the dataTypes - prev = current; - current = dataTypes[ i ]; - - // If current is auto dataType, update it to prev - if( current === "*" ) { - current = prev; - // If no auto and dataTypes are actually different - } else if ( prev !== "*" && prev !== current ) { - - // Get the converter - conversion = prev + " " + current; - conv = converters[ conversion ] || converters[ "* " + current ]; - - // If there is no direct converter, search transitively - if ( !conv ) { - conv2 = undefined; - for( conv1 in converters ) { - tmp = conv1.split( " " ); - if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) { - conv2 = converters[ tmp[1] + " " + current ]; - if ( conv2 ) { - conv1 = converters[ conv1 ]; - if ( conv1 === true ) { - conv = conv2; - } else if ( conv2 === true ) { - conv = conv1; - } - break; - } - } - } - } - // If we found no converter, dispatch an error - if ( !( conv || conv2 ) ) { - jQuery.error( "No conversion from " + conversion.replace(" "," to ") ); - } - // If found converter is not an equivalence - if ( conv !== true ) { - // Convert with 1 or 2 converters accordingly - response = conv ? conv( response ) : conv2( conv1(response) ); - } - } - } - return response; -} - - - - -var jsc = jQuery.now(), - jsre = /(\=)\?(&|$)|\?\?/i; - -// Default jsonp settings -jQuery.ajaxSetup({ - jsonp: "callback", - jsonpCallback: function() { - return jQuery.expando + "_" + ( jsc++ ); - } -}); - -// Detect, normalize options and install callbacks for jsonp requests -jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { - - var inspectData = s.contentType === "application/x-www-form-urlencoded" && - ( typeof s.data === "string" ); - - if ( s.dataTypes[ 0 ] === "jsonp" || - s.jsonp !== false && ( jsre.test( s.url ) || - inspectData && jsre.test( s.data ) ) ) { - - var responseContainer, - jsonpCallback = s.jsonpCallback = - jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, - previous = window[ jsonpCallback ], - url = s.url, - data = s.data, - replace = "$1" + jsonpCallback + "$2"; - - if ( s.jsonp !== false ) { - url = url.replace( jsre, replace ); - if ( s.url === url ) { - if ( inspectData ) { - data = data.replace( jsre, replace ); - } - if ( s.data === data ) { - // Add callback manually - url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback; - } - } - } - - s.url = url; - s.data = data; - - // Install callback - window[ jsonpCallback ] = function( response ) { - responseContainer = [ response ]; - }; - - // Clean-up function - jqXHR.always(function() { - // Set callback back to previous value - window[ jsonpCallback ] = previous; - // Call if it was a function and we have a response - if ( responseContainer && jQuery.isFunction( previous ) ) { - window[ jsonpCallback ]( responseContainer[ 0 ] ); - } - }); - - // Use data converter to retrieve json after script execution - s.converters["script json"] = function() { - if ( !responseContainer ) { - jQuery.error( jsonpCallback + " was not called" ); - } - return responseContainer[ 0 ]; - }; - - // force json dataType - s.dataTypes[ 0 ] = "json"; - - // Delegate to script - return "script"; - } -}); - - - - -// Install script dataType -jQuery.ajaxSetup({ - accepts: { - script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /javascript|ecmascript/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -}); - -// Handle cache's special case and global -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - s.global = false; - } -}); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function(s) { - - // This transport only deals with cross domain requests - if ( s.crossDomain ) { - - var script, - head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; - - return { - - send: function( _, callback ) { - - script = document.createElement( "script" ); - - script.async = "async"; - - if ( s.scriptCharset ) { - script.charset = s.scriptCharset; - } - - script.src = s.url; - - // Attach handlers for all browsers - script.onload = script.onreadystatechange = function( _, isAbort ) { - - if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { - - // Handle memory leak in IE - script.onload = script.onreadystatechange = null; - - // Remove the script - if ( head && script.parentNode ) { - head.removeChild( script ); - } - - // Dereference the script - script = undefined; - - // Callback if not abort - if ( !isAbort ) { - callback( 200, "success" ); - } - } - }; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709 and #4378). - head.insertBefore( script, head.firstChild ); - }, - - abort: function() { - if ( script ) { - script.onload( 0, 1 ); - } - } - }; - } -}); - - - - -var // #5280: Internet Explorer will keep connections alive if we don't abort on unload - xhrOnUnloadAbort = window.ActiveXObject ? function() { - // Abort all pending requests - for ( var key in xhrCallbacks ) { - xhrCallbacks[ key ]( 0, 1 ); - } - } : false, - xhrId = 0, - xhrCallbacks; - -// Functions to create xhrs -function createStandardXHR() { - try { - return new window.XMLHttpRequest(); - } catch( e ) {} -} - -function createActiveXHR() { - try { - return new window.ActiveXObject( "Microsoft.XMLHTTP" ); - } catch( e ) {} -} - -// Create the request object -// (This is still attached to ajaxSettings for backward compatibility) -jQuery.ajaxSettings.xhr = window.ActiveXObject ? - /* Microsoft failed to properly - * implement the XMLHttpRequest in IE7 (can't request local files), - * so we use the ActiveXObject when it is available - * Additionally XMLHttpRequest can be disabled in IE7/IE8 so - * we need a fallback. - */ - function() { - return !this.isLocal && createStandardXHR() || createActiveXHR(); - } : - // For all other browsers, use the standard XMLHttpRequest object - createStandardXHR; - -// Determine support properties -(function( xhr ) { - jQuery.extend( jQuery.support, { - ajax: !!xhr, - cors: !!xhr && ( "withCredentials" in xhr ) - }); -})( jQuery.ajaxSettings.xhr() ); - -// Create transport if the browser can provide an xhr -if ( jQuery.support.ajax ) { - - jQuery.ajaxTransport(function( s ) { - // Cross domain only allowed if supported through XMLHttpRequest - if ( !s.crossDomain || jQuery.support.cors ) { - - var callback; - - return { - send: function( headers, complete ) { - - // Get a new xhr - var xhr = s.xhr(), - handle, - i; - - // Open the socket - // Passing null username, generates a login popup on Opera (#2865) - if ( s.username ) { - xhr.open( s.type, s.url, s.async, s.username, s.password ); - } else { - xhr.open( s.type, s.url, s.async ); - } - - // Apply custom fields if provided - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !s.crossDomain && !headers["X-Requested-With"] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Need an extra try/catch for cross domain requests in Firefox 3 - try { - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - } catch( _ ) {} - - // Do send the request - // This may raise an exception which is actually - // handled in jQuery.ajax (so no try/catch here) - xhr.send( ( s.hasContent && s.data ) || null ); - - // Listener - callback = function( _, isAbort ) { - - var status, - statusText, - responseHeaders, - responses, - xml; - - // Firefox throws exceptions when accessing properties - // of an xhr when a network error occured - // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) - try { - - // Was never called and is aborted or complete - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - - // Only called once - callback = undefined; - - // Do not keep as active anymore - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } - } - - // If it's an abort - if ( isAbort ) { - // Abort it manually if needed - if ( xhr.readyState !== 4 ) { - xhr.abort(); - } - } else { - status = xhr.status; - responseHeaders = xhr.getAllResponseHeaders(); - responses = {}; - xml = xhr.responseXML; - - // Construct response list - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } - responses.text = xhr.responseText; - - // Firefox throws an exception when accessing - // statusText for faulty cross-domain requests - try { - statusText = xhr.statusText; - } catch( e ) { - // We normalize with Webkit giving an empty statusText - statusText = ""; - } - - // Filter status for non standard behaviors - - // If the request is local and we have data: assume a success - // (success with no data won't get notified, that's the best we - // can do given current implementations) - if ( !status && s.isLocal && !s.crossDomain ) { - status = responses.text ? 200 : 404; - // IE - #1450: sometimes returns 1223 when it should be 204 - } else if ( status === 1223 ) { - status = 204; - } - } - } - } catch( firefoxAccessException ) { - if ( !isAbort ) { - complete( -1, firefoxAccessException ); - } - } - - // Call complete if needed - if ( responses ) { - complete( status, statusText, responses, responseHeaders ); - } - }; - - // if we're in sync mode or it's in cache - // and has been retrieved directly (IE6 & IE7) - // we need to manually fire the callback - if ( !s.async || xhr.readyState === 4 ) { - callback(); - } else { - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // Create the active xhrs callbacks list if needed - // and attach the unload handler - if ( !xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); - } - // Add to list of active xhrs callbacks - xhrCallbacks[ handle ] = callback; - } - xhr.onreadystatechange = callback; - } - }, - - abort: function() { - if ( callback ) { - callback(0,1); - } - } - }; - } - }); -} - - - - -var elemdisplay = {}, - iframe, iframeDoc, - rfxtypes = /^(?:toggle|show|hide)$/, - rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i, - timerId, - fxAttrs = [ - // height animations - [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ], - // width animations - [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ], - // opacity animations - [ "opacity" ] - ], - fxNow; - -jQuery.fn.extend({ - show: function( speed, easing, callback ) { - var elem, display; - - if ( speed || speed === 0 ) { - return this.animate( genFx("show", 3), speed, easing, callback); - - } else { - for ( var i = 0, j = this.length; i < j; i++ ) { - elem = this[i]; - - if ( elem.style ) { - display = elem.style.display; - - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !jQuery._data(elem, "olddisplay") && display === "none" ) { - display = elem.style.display = ""; - } - - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( display === "" && jQuery.css( elem, "display" ) === "none" ) { - jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName)); - } - } - } - - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( i = 0; i < j; i++ ) { - elem = this[i]; - - if ( elem.style ) { - display = elem.style.display; - - if ( display === "" || display === "none" ) { - elem.style.display = jQuery._data(elem, "olddisplay") || ""; - } - } - } - - return this; - } - }, - - hide: function( speed, easing, callback ) { - if ( speed || speed === 0 ) { - return this.animate( genFx("hide", 3), speed, easing, callback); - - } else { - for ( var i = 0, j = this.length; i < j; i++ ) { - if ( this[i].style ) { - var display = jQuery.css( this[i], "display" ); - - if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) { - jQuery._data( this[i], "olddisplay", display ); - } - } - } - - // Set the display of the elements in a second loop - // to avoid the constant reflow - for ( i = 0; i < j; i++ ) { - if ( this[i].style ) { - this[i].style.display = "none"; - } - } - - return this; - } - }, - - // Save the old toggle function - _toggle: jQuery.fn.toggle, - - toggle: function( fn, fn2, callback ) { - var bool = typeof fn === "boolean"; - - if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { - this._toggle.apply( this, arguments ); - - } else if ( fn == null || bool ) { - this.each(function() { - var state = bool ? fn : jQuery(this).is(":hidden"); - jQuery(this)[ state ? "show" : "hide" ](); - }); - - } else { - this.animate(genFx("toggle", 3), fn, fn2, callback); - } - - return this; - }, - - fadeTo: function( speed, to, easing, callback ) { - return this.filter(":hidden").css("opacity", 0).show().end() - .animate({opacity: to}, speed, easing, callback); - }, - - animate: function( prop, speed, easing, callback ) { - var optall = jQuery.speed(speed, easing, callback); - - if ( jQuery.isEmptyObject( prop ) ) { - return this.each( optall.complete, [ false ] ); - } - - // Do not change referenced properties as per-property easing will be lost - prop = jQuery.extend( {}, prop ); - - return this[ optall.queue === false ? "each" : "queue" ](function() { - // XXX 'this' does not always have a nodeName when running the - // test suite - - if ( optall.queue === false ) { - jQuery._mark( this ); - } - - var opt = jQuery.extend( {}, optall ), - isElement = this.nodeType === 1, - hidden = isElement && jQuery(this).is(":hidden"), - name, val, p, - display, e, - parts, start, end, unit; - - // will store per property easing and be used to determine when an animation is complete - opt.animatedProperties = {}; - - for ( p in prop ) { - - // property name normalization - name = jQuery.camelCase( p ); - if ( p !== name ) { - prop[ name ] = prop[ p ]; - delete prop[ p ]; - } - - val = prop[ name ]; - - // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) - if ( jQuery.isArray( val ) ) { - opt.animatedProperties[ name ] = val[ 1 ]; - val = prop[ name ] = val[ 0 ]; - } else { - opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; - } - - if ( val === "hide" && hidden || val === "show" && !hidden ) { - return opt.complete.call( this ); - } - - if ( isElement && ( name === "height" || name === "width" ) ) { - // Make sure that nothing sneaks out - // Record all 3 overflow attributes because IE does not - // change the overflow attribute when overflowX and - // overflowY are set to the same value - opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; - - // Set display property to inline-block for height/width - // animations on inline elements that are having width/height - // animated - if ( jQuery.css( this, "display" ) === "inline" && - jQuery.css( this, "float" ) === "none" ) { - if ( !jQuery.support.inlineBlockNeedsLayout ) { - this.style.display = "inline-block"; - - } else { - display = defaultDisplay( this.nodeName ); - - // inline-level elements accept inline-block; - // block-level elements need to be inline with layout - if ( display === "inline" ) { - this.style.display = "inline-block"; - - } else { - this.style.display = "inline"; - this.style.zoom = 1; - } - } - } - } - } - - if ( opt.overflow != null ) { - this.style.overflow = "hidden"; - } - - for ( p in prop ) { - e = new jQuery.fx( this, opt, p ); - val = prop[ p ]; - - if ( rfxtypes.test(val) ) { - e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); - - } else { - parts = rfxnum.exec( val ); - start = e.cur(); - - if ( parts ) { - end = parseFloat( parts[2] ); - unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); - - // We need to compute starting value - if ( unit !== "px" ) { - jQuery.style( this, p, (end || 1) + unit); - start = ((end || 1) / e.cur()) * start; - jQuery.style( this, p, start + unit); - } - - // If a +=/-= token was provided, we're doing a relative animation - if ( parts[1] ) { - end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; - } - - e.custom( start, end, unit ); - - } else { - e.custom( start, val, "" ); - } - } - } - - // For JS strict compliance - return true; - }); - }, - - stop: function( clearQueue, gotoEnd ) { - if ( clearQueue ) { - this.queue([]); - } - - this.each(function() { - var timers = jQuery.timers, - i = timers.length; - // clear marker counters if we know they won't be - if ( !gotoEnd ) { - jQuery._unmark( true, this ); - } - while ( i-- ) { - if ( timers[i].elem === this ) { - if (gotoEnd) { - // force the next step to be the last - timers[i](true); - } - - timers.splice(i, 1); - } - } - }); - - // start the next in the queue if the last step wasn't forced - if ( !gotoEnd ) { - this.dequeue(); - } - - return this; - } - -}); - -// Animations created synchronously will run synchronously -function createFxNow() { - setTimeout( clearFxNow, 0 ); - return ( fxNow = jQuery.now() ); -} - -function clearFxNow() { - fxNow = undefined; -} - -// Generate parameters to create a standard animation -function genFx( type, num ) { - var obj = {}; - - jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() { - obj[ this ] = type; - }); - - return obj; -} - -// Generate shortcuts for custom animations -jQuery.each({ - slideDown: genFx("show", 1), - slideUp: genFx("hide", 1), - slideToggle: genFx("toggle", 1), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -}); - -jQuery.extend({ - speed: function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : { - complete: fn || !fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !jQuery.isFunction(easing) && easing - }; - - opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : - opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default; - - // Queueing - opt.old = opt.complete; - opt.complete = function( noUnmark ) { - if ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue !== false ) { - jQuery.dequeue( this ); - } else if ( noUnmark !== false ) { - jQuery._unmark( this ); - } - }; - - return opt; - }, - - easing: { - linear: function( p, n, firstNum, diff ) { - return firstNum + diff * p; - }, - swing: function( p, n, firstNum, diff ) { - return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum; - } - }, - - timers: [], - - fx: function( elem, options, prop ) { - this.options = options; - this.elem = elem; - this.prop = prop; - - options.orig = options.orig || {}; - } - -}); - -jQuery.fx.prototype = { - // Simple function for setting a style value - update: function() { - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this ); - }, - - // Get the current size - cur: function() { - if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) { - return this.elem[ this.prop ]; - } - - var parsed, - r = jQuery.css( this.elem, this.prop ); - // Empty strings, null, undefined and "auto" are converted to 0, - // complex values such as "rotate(1rad)" are returned as is, - // simple values such as "10px" are parsed to Float. - return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed; - }, - - // Start an animation from one number to another - custom: function( from, to, unit ) { - var self = this, - fx = jQuery.fx; - - this.startTime = fxNow || createFxNow(); - this.start = from; - this.end = to; - this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); - this.now = this.start; - this.pos = this.state = 0; - - function t( gotoEnd ) { - return self.step(gotoEnd); - } - - t.elem = this.elem; - - if ( t() && jQuery.timers.push(t) && !timerId ) { - timerId = setInterval( fx.tick, fx.interval ); - } - }, - - // Simple 'show' function - show: function() { - // Remember where we started, so that we can go back to it later - this.options.orig[this.prop] = jQuery.style( this.elem, this.prop ); - this.options.show = true; - - // Begin the animation - // Make sure that we start at a small width/height to avoid any - // flash of content - this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur()); - - // Start by showing the element - jQuery( this.elem ).show(); - }, - - // Simple 'hide' function - hide: function() { - // Remember where we started, so that we can go back to it later - this.options.orig[this.prop] = jQuery.style( this.elem, this.prop ); - this.options.hide = true; - - // Begin the animation - this.custom(this.cur(), 0); - }, - - // Each step of an animation - step: function( gotoEnd ) { - var t = fxNow || createFxNow(), - done = true, - elem = this.elem, - options = this.options, - i, n; - - if ( gotoEnd || t >= options.duration + this.startTime ) { - this.now = this.end; - this.pos = this.state = 1; - this.update(); - - options.animatedProperties[ this.prop ] = true; - - for ( i in options.animatedProperties ) { - if ( options.animatedProperties[i] !== true ) { - done = false; - } - } - - if ( done ) { - // Reset the overflow - if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) { - - jQuery.each( [ "", "X", "Y" ], function (index, value) { - elem.style[ "overflow" + value ] = options.overflow[index]; - }); - } - - // Hide the element if the "hide" operation was done - if ( options.hide ) { - jQuery(elem).hide(); - } - - // Reset the properties, if the item has been hidden or shown - if ( options.hide || options.show ) { - for ( var p in options.animatedProperties ) { - jQuery.style( elem, p, options.orig[p] ); - } - } - - // Execute the complete function - options.complete.call( elem ); - } - - return false; - - } else { - // classical easing cannot be used with an Infinity duration - if ( options.duration == Infinity ) { - this.now = t; - } else { - n = t - this.startTime; - this.state = n / options.duration; - - // Perform the easing function, defaults to swing - this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration ); - this.now = this.start + ((this.end - this.start) * this.pos); - } - // Perform the next step of the animation - this.update(); - } - - return true; - } -}; - -jQuery.extend( jQuery.fx, { - tick: function() { - for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) { - if ( !timers[i]() ) { - timers.splice(i--, 1); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - }, - - interval: 13, - - stop: function() { - clearInterval( timerId ); - timerId = null; - }, - - speeds: { - slow: 600, - fast: 200, - // Default speed - _default: 400 - }, - - step: { - opacity: function( fx ) { - jQuery.style( fx.elem, "opacity", fx.now ); - }, - - _default: function( fx ) { - if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) { - fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit; - } else { - fx.elem[ fx.prop ] = fx.now; - } - } - } -}); - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.animated = function( elem ) { - return jQuery.grep(jQuery.timers, function( fn ) { - return elem === fn.elem; - }).length; - }; -} - -// Try to restore the default display value of an element -function defaultDisplay( nodeName ) { - - if ( !elemdisplay[ nodeName ] ) { - - var body = document.body, - elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), - display = elem.css( "display" ); - - elem.remove(); - - // If the simple way fails, - // get element's real default display by attaching it to a temp iframe - if ( display === "none" || display === "" ) { - // No iframe to use yet, so create it - if ( !iframe ) { - iframe = document.createElement( "iframe" ); - iframe.frameBorder = iframe.width = iframe.height = 0; - } - - body.appendChild( iframe ); - - // Create a cacheable copy of the iframe document on first call. - // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML - // document to it; WebKit & Firefox won't allow reusing the iframe document. - if ( !iframeDoc || !iframe.createElement ) { - iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" ); - iframeDoc.close(); - } - - elem = iframeDoc.createElement( nodeName ); - - iframeDoc.body.appendChild( elem ); - - display = jQuery.css( elem, "display" ); - - body.removeChild( iframe ); - } - - // Store the correct default display - elemdisplay[ nodeName ] = display; - } - - return elemdisplay[ nodeName ]; -} - - - - -var rtable = /^t(?:able|d|h)$/i, - rroot = /^(?:body|html)$/i; - -if ( "getBoundingClientRect" in document.documentElement ) { - jQuery.fn.offset = function( options ) { - var elem = this[0], box; - - if ( options ) { - return this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - if ( !elem || !elem.ownerDocument ) { - return null; - } - - if ( elem === elem.ownerDocument.body ) { - return jQuery.offset.bodyOffset( elem ); - } - - try { - box = elem.getBoundingClientRect(); - } catch(e) {} - - var doc = elem.ownerDocument, - docElem = doc.documentElement; - - // Make sure we're not dealing with a disconnected DOM node - if ( !box || !jQuery.contains( docElem, elem ) ) { - return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; - } - - var body = doc.body, - win = getWindow(doc), - clientTop = docElem.clientTop || body.clientTop || 0, - clientLeft = docElem.clientLeft || body.clientLeft || 0, - scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, - scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, - top = box.top + scrollTop - clientTop, - left = box.left + scrollLeft - clientLeft; - - return { top: top, left: left }; - }; - -} else { - jQuery.fn.offset = function( options ) { - var elem = this[0]; - - if ( options ) { - return this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - if ( !elem || !elem.ownerDocument ) { - return null; - } - - if ( elem === elem.ownerDocument.body ) { - return jQuery.offset.bodyOffset( elem ); - } - - jQuery.offset.initialize(); - - var computedStyle, - offsetParent = elem.offsetParent, - prevOffsetParent = elem, - doc = elem.ownerDocument, - docElem = doc.documentElement, - body = doc.body, - defaultView = doc.defaultView, - prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle, - top = elem.offsetTop, - left = elem.offsetLeft; - - while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { - if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) { - break; - } - - computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle; - top -= elem.scrollTop; - left -= elem.scrollLeft; - - if ( elem === offsetParent ) { - top += elem.offsetTop; - left += elem.offsetLeft; - - if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { - top += parseFloat( computedStyle.borderTopWidth ) || 0; - left += parseFloat( computedStyle.borderLeftWidth ) || 0; - } - - prevOffsetParent = offsetParent; - offsetParent = elem.offsetParent; - } - - if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) { - top += parseFloat( computedStyle.borderTopWidth ) || 0; - left += parseFloat( computedStyle.borderLeftWidth ) || 0; - } - - prevComputedStyle = computedStyle; - } - - if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) { - top += body.offsetTop; - left += body.offsetLeft; - } - - if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) { - top += Math.max( docElem.scrollTop, body.scrollTop ); - left += Math.max( docElem.scrollLeft, body.scrollLeft ); - } - - return { top: top, left: left }; - }; -} - -jQuery.offset = { - initialize: function() { - var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0, - html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>"; - - jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } ); - - container.innerHTML = html; - body.insertBefore( container, body.firstChild ); - innerDiv = container.firstChild; - checkDiv = innerDiv.firstChild; - td = innerDiv.nextSibling.firstChild.firstChild; - - this.doesNotAddBorder = (checkDiv.offsetTop !== 5); - this.doesAddBorderForTableAndCells = (td.offsetTop === 5); - - checkDiv.style.position = "fixed"; - checkDiv.style.top = "20px"; - - // safari subtracts parent border width here which is 5px - this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15); - checkDiv.style.position = checkDiv.style.top = ""; - - innerDiv.style.overflow = "hidden"; - innerDiv.style.position = "relative"; - - this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5); - - this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop); - - body.removeChild( container ); - jQuery.offset.initialize = jQuery.noop; - }, - - bodyOffset: function( body ) { - var top = body.offsetTop, - left = body.offsetLeft; - - jQuery.offset.initialize(); - - if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) { - top += parseFloat( jQuery.css(body, "marginTop") ) || 0; - left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; - } - - return { top: top, left: left }; - }, - - setOffset: function( elem, options, i ) { - var position = jQuery.css( elem, "position" ); - - // set position first, in-case top/left are set even on static elem - if ( position === "static" ) { - elem.style.position = "relative"; - } - - var curElem = jQuery( elem ), - curOffset = curElem.offset(), - curCSSTop = jQuery.css( elem, "top" ), - curCSSLeft = jQuery.css( elem, "left" ), - calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, - props = {}, curPosition = {}, curTop, curLeft; - - // need to be able to calculate position if either top or left is auto and position is either absolute or fixed - if ( calculatePosition ) { - curPosition = curElem.position(); - curTop = curPosition.top; - curLeft = curPosition.left; - } else { - curTop = parseFloat( curCSSTop ) || 0; - curLeft = parseFloat( curCSSLeft ) || 0; - } - - if ( jQuery.isFunction( options ) ) { - options = options.call( elem, i, curOffset ); - } - - if (options.top != null) { - props.top = (options.top - curOffset.top) + curTop; - } - if (options.left != null) { - props.left = (options.left - curOffset.left) + curLeft; - } - - if ( "using" in options ) { - options.using.call( elem, props ); - } else { - curElem.css( props ); - } - } -}; - - -jQuery.fn.extend({ - position: function() { - if ( !this[0] ) { - return null; - } - - var elem = this[0], - - // Get *real* offsetParent - offsetParent = this.offsetParent(), - - // Get correct offsets - offset = this.offset(), - parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); - - // Subtract element margins - // note: when an element has margin: auto the offsetLeft and marginLeft - // are the same in Safari causing offset.left to incorrectly be 0 - offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; - offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; - - // Add offsetParent borders - parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; - parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; - - // Subtract the two offsets - return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left - }; - }, - - offsetParent: function() { - return this.map(function() { - var offsetParent = this.offsetParent || document.body; - while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent; - }); - } -}); - - -// Create scrollLeft and scrollTop methods -jQuery.each( ["Left", "Top"], function( i, name ) { - var method = "scroll" + name; - - jQuery.fn[ method ] = function( val ) { - var elem, win; - - if ( val === undefined ) { - elem = this[ 0 ]; - - if ( !elem ) { - return null; - } - - win = getWindow( elem ); - - // Return the scroll offset - return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : - jQuery.support.boxModel && win.document.documentElement[ method ] || - win.document.body[ method ] : - elem[ method ]; - } - - // Set the scroll offset - return this.each(function() { - win = getWindow( this ); - - if ( win ) { - win.scrollTo( - !i ? val : jQuery( win ).scrollLeft(), - i ? val : jQuery( win ).scrollTop() - ); - - } else { - this[ method ] = val; - } - }); - }; -}); - -function getWindow( elem ) { - return jQuery.isWindow( elem ) ? - elem : - elem.nodeType === 9 ? - elem.defaultView || elem.parentWindow : - false; -} - - - - -// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods -jQuery.each([ "Height", "Width" ], function( i, name ) { - - var type = name.toLowerCase(); - - // innerHeight and innerWidth - jQuery.fn[ "inner" + name ] = function() { - var elem = this[0]; - return elem && elem.style ? - parseFloat( jQuery.css( elem, type, "padding" ) ) : - null; - }; - - // outerHeight and outerWidth - jQuery.fn[ "outer" + name ] = function( margin ) { - var elem = this[0]; - return elem && elem.style ? - parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : - null; - }; - - jQuery.fn[ type ] = function( size ) { - // Get window width or height - var elem = this[0]; - if ( !elem ) { - return size == null ? null : this; - } - - if ( jQuery.isFunction( size ) ) { - return this.each(function( i ) { - var self = jQuery( this ); - self[ type ]( size.call( this, i, self[ type ]() ) ); - }); - } - - if ( jQuery.isWindow( elem ) ) { - // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode - // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat - var docElemProp = elem.document.documentElement[ "client" + name ], - body = elem.document.body; - return elem.document.compatMode === "CSS1Compat" && docElemProp || - body && body[ "client" + name ] || docElemProp; - - // Get document width or height - } else if ( elem.nodeType === 9 ) { - // Either scroll[Width/Height] or offset[Width/Height], whichever is greater - return Math.max( - elem.documentElement["client" + name], - elem.body["scroll" + name], elem.documentElement["scroll" + name], - elem.body["offset" + name], elem.documentElement["offset" + name] - ); - - // Get or set width or height on the element - } else if ( size === undefined ) { - var orig = jQuery.css( elem, type ), - ret = parseFloat( orig ); - - return jQuery.isNaN( ret ) ? orig : ret; - - // Set the width or height on the element (default to pixels if value is unitless) - } else { - return this.css( type, typeof size === "string" ? size : size + "px" ); - } - }; - -}); - - -// Expose jQuery to the global object -window.jQuery = window.$ = jQuery; -})(window); diff --git a/addons/web/static/lib/novajs/test/qunit.css b/addons/web/static/lib/novajs/test/qunit.css deleted file mode 100755 index bcecc4c0daf..00000000000 --- a/addons/web/static/lib/novajs/test/qunit.css +++ /dev/null @@ -1,226 +0,0 @@ -/** - * QUnit v1.2.0 - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests ol { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; -} diff --git a/addons/web/static/lib/novajs/test/qunit.js b/addons/web/static/lib/novajs/test/qunit.js deleted file mode 100755 index 6d2a8a7b8ab..00000000000 --- a/addons/web/static/lib/novajs/test/qunit.js +++ /dev/null @@ -1,1597 +0,0 @@ -/** - * QUnit v1.2.0 - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -(function(window) { - -var defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - try { - return !!sessionStorage.getItem; - } catch(e) { - return false; - } - })() -}; - -var testId = 0, - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty; - -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { - this.name = name; - this.testName = testName; - this.expected = expected; - this.testEnvironmentArg = testEnvironmentArg; - this.async = async; - this.callback = callback; - this.assertions = []; -}; -Test.prototype = { - init: function() { - var tests = id("qunit-tests"); - if (tests) { - var b = document.createElement("strong"); - b.innerHTML = "Running " + this.name; - var li = document.createElement("li"); - li.appendChild( b ); - li.className = "running"; - li.id = this.id = "test-output" + testId++; - tests.appendChild( li ); - } - }, - setup: function() { - if (this.module != config.previousModule) { - if ( config.previousModule ) { - runLoggingCallbacks('moduleDone', QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( 'moduleStart', QUnit, { - name: this.module - } ); - } - - config.current = this; - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment); - if (this.testEnvironmentArg) { - extend(this.testEnvironment, this.testEnvironmentArg); - } - - runLoggingCallbacks( 'testStart', QUnit, { - name: this.testName, - module: this.module - }); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - try { - if ( !config.pollution ) { - saveGlobal(); - } - - this.testEnvironment.setup.call(this.testEnvironment); - } catch(e) { - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); - } - }, - run: function() { - config.current = this; - if ( this.async ) { - QUnit.stop(); - } - - if ( config.notrycatch ) { - this.callback.call(this.testEnvironment); - return; - } - try { - this.callback.call(this.testEnvironment); - } catch(e) { - fail("Test " + this.testName + " died, exception and test follows", e, this.callback); - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - try { - this.testEnvironment.teardown.call(this.testEnvironment); - checkPollution(); - } catch(e) { - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); - } - }, - finish: function() { - config.current = this; - if ( this.expected != null && this.expected != this.assertions.length ) { - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); - } - - var good = 0, bad = 0, - tests = id("qunit-tests"); - - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); - - for ( var i = 0; i < this.assertions.length; i++ ) { - var assertion = this.assertions[i]; - - var li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if (bad) { - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); - } else { - sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); - } - } - - if (bad == 0) { - ol.style.display = "none"; - } - - var b = document.createElement("strong"); - b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; - - var a = document.createElement("a"); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function(e) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - } - }); - - var li = id(this.id); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( ol ); - - } else { - for ( var i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - try { - QUnit.reset(); - } catch(e) { - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); - } - - runLoggingCallbacks( 'testDone', QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - } ); - }, - - queue: function() { - var test = this; - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - // defer when previous test run passed, if storage is available - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); - if (bad) { - run(); - } else { - synchronize(run, true); - }; - } - -}; - -var QUnit = { - - // call on start of module test to prepend name to all tests - module: function(name, testEnvironment) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; - }, - - asyncTest: function(testName, expected, callback) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test(testName, expected, callback, true); - }, - - test: function(testName, expected, callback, async) { - var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - // is 2nd argument a testEnvironment? - if ( expected && typeof expected === 'object') { - testEnvironmentArg = expected; - expected = null; - } - - if ( config.currentModule ) { - name = '<span class="module-name">' + config.currentModule + "</span>: " + name; - } - - if ( !validTest(config.currentModule + ": " + testName) ) { - return; - } - - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); - test.module = config.currentModule; - test.moduleTestEnvironment = config.currentModuleTestEnviroment; - test.queue(); - }, - - /** - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - */ - expect: function(asserts) { - config.current.expected = asserts; - }, - - /** - * Asserts true. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function(a, msg) { - a = !!a; - var details = { - result: a, - message: msg - }; - msg = escapeInnerText(msg); - runLoggingCallbacks( 'log', QUnit, details ); - config.current.assertions.push({ - result: a, - message: msg - }); - }, - - /** - * Checks that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * - * Prefered to ok( actual == expected, message ) - * - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); - * - * @param Object actual - * @param Object expected - * @param String message (optional) - */ - equal: function(actual, expected, message) { - QUnit.push(expected == actual, actual, expected, message); - }, - - notEqual: function(actual, expected, message) { - QUnit.push(expected != actual, actual, expected, message); - }, - - deepEqual: function(actual, expected, message) { - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); - }, - - notDeepEqual: function(actual, expected, message) { - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); - }, - - strictEqual: function(actual, expected, message) { - QUnit.push(expected === actual, actual, expected, message); - }, - - notStrictEqual: function(actual, expected, message) { - QUnit.push(expected !== actual, actual, expected, message); - }, - - raises: function(block, expected, message) { - var actual, ok = false; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block(); - } catch (e) { - actual = e; - } - - if (actual) { - // we don't want to validate thrown error - if (!expected) { - ok = true; - // expected is a regexp - } else if (QUnit.objectType(expected) === "regexp") { - ok = expected.test(actual); - // expected is a constructor - } else if (actual instanceof expected) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if (expected.call({}, actual) === true) { - ok = true; - } - } - - QUnit.ok(ok, message); - }, - - start: function(count) { - config.semaphore -= count || 1; - if (config.semaphore > 0) { - // don't start until equal number of stop-calls - return; - } - if (config.semaphore < 0) { - // ignore if start is called more often then stop - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if (config.semaphore > 0) { - return; - } - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(true); - }, 13); - } else { - config.blocking = false; - process(true); - } - }, - - stop: function(count) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout(config.timeout); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout); - } - } -}; - -//We want access to the constructor's prototype -(function() { - function F(){}; - F.prototype = QUnit; - QUnit = new F(); - //Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -})(); - -// Backwards compatibility, deprecated -QUnit.equals = QUnit.equal; -QUnit.same = QUnit.deepEqual; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - urlConfig: ['noglobals', 'notrycatch'], - - //logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( var i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - config.filter = urlParams.filter; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = !!(location.protocol === 'file:'); -})(); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} else { - extend(exports, QUnit); - exports.QUnit = QUnit; -} - -// define these after exposing globals to keep them in these QUnit namespace only -extend(QUnit, { - config: config, - - // Initialize the configuration options - init: function() { - extend(config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date, - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var tests = id( "qunit-tests" ), - banner = id( "qunit-banner" ), - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = 'Running...<br/> '; - } - }, - - /** - * Resets the test setup. Useful for tests that modify the DOM. - * - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - */ - reset: function() { - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - var main = id( 'qunit-fixture' ); - if ( main ) { - main.innerHTML = config.fixture; - } - } - }, - - /** - * Trigger an event on an element. - * - * @example triggerEvent( document.body, "click" ); - * - * @param DOMElement elem - * @param String type - */ - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) == type; - }, - - objectType: function( obj ) { - if (typeof obj === "undefined") { - return "undefined"; - - // consider: typeof null === object - } - if (obj === null) { - return "null"; - } - - var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ''; - - switch (type) { - case 'Number': - if (isNaN(obj)) { - return "nan"; - } else { - return "number"; - } - case 'String': - case 'Boolean': - case 'Array': - case 'Date': - case 'RegExp': - case 'Function': - return type.toLowerCase(); - } - if (typeof obj === "object") { - return "object"; - } - return undefined; - }, - - push: function(result, actual, expected, message) { - var details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeInnerText(message) || (result ? "okay" : "failed"); - message = '<span class="test-message">' + message + "</span>"; - expected = escapeInnerText(QUnit.jsDump.parse(expected)); - actual = escapeInnerText(QUnit.jsDump.parse(actual)); - var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>'; - if (actual != expected) { - output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>'; - output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>'; - } - if (!result) { - var source = sourceFromStacktrace(); - if (source) { - details.source = source; - output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>'; - } - } - output += "</table>"; - - runLoggingCallbacks( 'log', QUnit, details ); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var querystring = "?", - key; - for ( key in params ) { - if ( !hasOwn.call( params, key ) ) { - continue; - } - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent -}); - -//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later -//Doing this allows us to tell if the following methods have been overwritten on the actual -//QUnit object, which is a deprecated way of using the callbacks. -extend(QUnit.constructor.prototype, { - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback('begin'), - // done: { failed, passed, total, runtime } - done: registerLoggingCallback('done'), - // log: { result, actual, expected, message } - log: registerLoggingCallback('log'), - // testStart: { name } - testStart: registerLoggingCallback('testStart'), - // testDone: { name, failed, passed, total } - testDone: registerLoggingCallback('testDone'), - // moduleStart: { name } - moduleStart: registerLoggingCallback('moduleStart'), - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback('moduleDone') -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( 'begin', QUnit, {} ); - - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - var urlConfigHtml = '', len = config.urlConfig.length; - for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) { - config[val] = QUnit.urlParams[val]; - urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>'; - } - - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - var banner = id("qunit-header"); - if ( banner ) { - banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } - - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - addEvent( filter, "click", function() { - var ol = document.getElementById("qunit-tests"); - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace(/ hidepass /, " "); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); - } else { - sessionStorage.removeItem("qunit-filter-passed-tests"); - } - } - }); - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { - filter.checked = true; - var ol = document.getElementById("qunit-tests"); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - var label = document.createElement("label"); - label.setAttribute("for", "qunit-filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - } - - var main = id('qunit-fixture'); - if ( main ) { - config.fixture = main.innerHTML; - } - - if (config.autostart) { - QUnit.start(); - } -}; - -addEvent(window, "load", QUnit.load); - -// addEvent(window, "error") gives us a useless event object -window.onerror = function( message, file, line ) { - if ( QUnit.config.current ) { - ok( false, message + ", " + file + ":" + line ); - } else { - test( "global failure", function() { - ok( false, message + ", " + file + ":" + line ); - }); - } -}; - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( 'moduleDone', QUnit, { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - runtime = +new Date - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - 'Tests completed in ', - runtime, - ' milliseconds.<br/>', - '<span class="passed">', - passed, - '</span> tests of <span class="total">', - config.stats.all, - '</span> passed, <span class="failed">', - config.stats.bad, - '</span> failed.' - ].join(''); - - if ( banner ) { - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - (config.stats.bad ? "\u2716" : "\u2714"), - document.title.replace(/^[\u2714\u2716] /i, "") - ].join(" "); - } - - runLoggingCallbacks( 'done', QUnit, { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - } ); -} - -function validTest( name ) { - var filter = config.filter, - run = false; - - if ( !filter ) { - return true; - } - - var not = filter.charAt( 0 ) === "!"; - if ( not ) { - filter = filter.slice( 1 ); - } - - if ( name.indexOf( filter ) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - - return run; -} - -// so far supports only Firefox, Chrome and Opera (buggy) -// could be extended in the future to use something like https://github.com/csnover/TraceKit -function sourceFromStacktrace() { - try { - throw new Error(); - } catch ( e ) { - if (e.stacktrace) { - // Opera - return e.stacktrace.split("\n")[6]; - } else if (e.stack) { - // Firefox, Chrome - return e.stack.split("\n")[4]; - } else if (e.sourceURL) { - // Safari, PhantomJS - // TODO sourceURL points at the 'throw new Error' line above, useless - //return e.sourceURL + ":" + e.line; - } - } -} - -function escapeInnerText(s) { - if (!s) { - return ""; - } - s = s + ""; - return s.replace(/[\&<>]/g, function(s) { - switch(s) { - case "&": return "&"; - case "<": return "<"; - case ">": return ">"; - default: return s; - } - }); -} - -function synchronize( callback, last ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process(last); - } -} - -function process( last ) { - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { - config.queue.shift()(); - } else { - window.setTimeout( function(){ - process( last ); - }, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - if ( !hasOwn.call( window, key ) ) { - continue; - } - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var old = config.pollution; - saveGlobal(); - - var newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - var deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; -} - -function fail(message, exception, callback) { - if ( typeof console !== "undefined" && console.error && console.warn ) { - console.error(message); - console.error(exception); - console.warn(callback.toString()); - - } else if ( window.opera && opera.postError ) { - opera.postError(message, exception, callback.toString); - } -} - -function extend(a, b) { - for ( var prop in b ) { - if ( b[prop] === undefined ) { - delete a[prop]; - - // Avoid "Member not found" error in IE8 caused by setting window.constructor - } else if ( prop !== "constructor" || a !== window ) { - a[prop] = b[prop]; - } - } - - return a; -} - -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); -} - -function registerLoggingCallback(key){ - return function(callback){ - config[key].push( callback ); - }; -} - -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks(key, scope, args) { - //debugger; - var callbacks; - if ( QUnit.hasOwnProperty(key) ) { - QUnit[key].call(scope, args); - } else { - callbacks = config[key]; - for( var i = 0; i < callbacks.length; i++ ) { - callbacks[i].call( scope, args ); - } - } -} - -// Test for equality any JavaScript type. -// Author: Philippe Rathé <prathe@gmail.com> -QUnit.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = QUnit.objectType(o); - if (prop) { - if (QUnit.objectType(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var getProto = Object.getPrototypeOf || function (obj) { - return obj.__proto__; - }; - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string" : useStrictEquality, - "boolean" : useStrictEquality, - "number" : useStrictEquality, - "null" : useStrictEquality, - "undefined" : useStrictEquality, - - "nan" : function(b) { - return isNaN(b); - }, - - "date" : function(b, a) { - return QUnit.objectType(b) === "date" - && a.valueOf() === b.valueOf(); - }, - - "regexp" : function(b, a) { - return QUnit.objectType(b) === "regexp" - && a.source === b.source && // the regex itself - a.global === b.global && // and its modifers - // (gmi) ... - a.ignoreCase === b.ignoreCase - && a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function" : function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array" : function(b, a) { - var i, j, loop; - var len; - - // b could be an object literal here - if (!(QUnit.objectType(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push(a); - for (i = 0; i < len; i++) { - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) { - loop = true;// dont rewalk array - } - } - if (!loop && !innerEquiv(a[i], b[i])) { - parents.pop(); - return false; - } - } - parents.pop(); - return true; - }, - - "object" : function(b, a) { - var i, j, loop; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of - // strings - - // comparing constructors is more strict than using - // instanceof - if (a.constructor !== b.constructor) { - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if (!((getProto(a) === null && getProto(b) === Object.prototype) || - (getProto(b) === null && getProto(a) === Object.prototype))) - { - return false; - } - } - - // stack constructor before traversing properties - callers.push(a.constructor); - // track reference to avoid circular references - parents.push(a); - - for (i in a) { // be strict: don't ensures hasOwnProperty - // and go deep - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) - loop = true; // don't go down the same path - // twice - } - aProperties.push(i); // collect a's properties - - if (!loop && !innerEquiv(a[i], b[i])) { - eq = false; - break; - } - } - - callers.pop(); // unstack, we are done - parents.pop(); - - for (i in b) { - bProperties.push(i); // collect b's properties - } - - // Ensures identical properties name - return eq - && innerEquiv(aProperties.sort(), bProperties - .sort()); - } - }; - }(); - - innerEquiv = function() { // can take multiple arguments - var args = Array.prototype.slice.apply(arguments); - if (args.length < 2) { - return true; // end transition - } - - return (function(a, b) { - if (a === b) { - return true; // catch the most you can - } else if (a === null || b === null || typeof a === "undefined" - || typeof b === "undefined" - || QUnit.objectType(a) !== QUnit.objectType(b)) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [ b, a ]); - } - - // apply transition with (1..n) arguments - })(args[0], args[1]) - && arguments.callee.apply(this, args.splice(1, - args.length - 1)); - }; - - return innerEquiv; - -}(); - -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace(/"/g, '\\"') + '"'; - }; - function literal( o ) { - return o + ''; - }; - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) - arr = arr.join( ',' + s + inner ); - if ( !arr ) - return pre + post; - return [ pre, inner + arr, base + post ].join(s); - }; - function array( arr, stack ) { - var i = arr.length, ret = Array(i); - this.up(); - while ( i-- ) - ret[i] = this.parse( arr[i] , undefined , stack); - this.down(); - return join( '[', ret, ']' ); - }; - - var reName = /^function (\w+)/; - - var jsDump = { - parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance - stack = stack || [ ]; - var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - var inStack = inArray(obj, stack); - if (inStack != -1) { - return 'recursion('+(inStack - stack.length)+')'; - } - //else - if (type == 'function') { - stack.push(obj); - var res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - // else - return (type == 'string') ? parser : this.parsers.error; - }, - typeOf:function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if (typeof obj === "undefined") { - type = "undefined"; - } else if (QUnit.is("RegExp", obj)) { - type = "regexp"; - } else if (QUnit.is("Date", obj)) { - type = "date"; - } else if (QUnit.is("Function", obj)) { - type = "function"; - } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { - type = "window"; - } else if (obj.nodeType === 9) { - type = "document"; - } else if (obj.nodeType) { - type = "node"; - } else if ( - // native arrays - toString.call( obj ) === "[object Array]" || - // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) - ) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator:function() { - return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' '; - }, - indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) - return ''; - var chr = this.indentChar; - if ( this.HTML ) - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - return Array( this._depth_ + (extra||0) ).join(chr); - }, - up:function( a ) { - this._depth_ += a || 1; - }, - down:function( a ) { - this._depth_ -= a || 1; - }, - setParser:function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote:quote, - literal:literal, - join:join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers:{ - window: '[Window]', - document: '[Document]', - error:'[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null':'null', - 'undefined':'undefined', - 'function':function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) - ret += ' ' + name; - ret += '('; - - ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); - }, - array: array, - nodelist: array, - arguments: array, - object:function( map, stack ) { - var ret = [ ]; - QUnit.jsDump.up(); - for ( var key in map ) { - var val = map[key]; - ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack)); - } - QUnit.jsDump.down(); - return join( '{', ret, '}' ); - }, - node:function( node ) { - var open = QUnit.jsDump.HTML ? '<' : '<', - close = QUnit.jsDump.HTML ? '>' : '>'; - - var tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( var a in QUnit.jsDump.DOMAttrs ) { - var val = node[QUnit.jsDump.DOMAttrs[a]]; - if ( val ) - ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); - } - return ret + close + open + '/' + tag + close; - }, - functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) return ''; - - var args = Array(l); - while ( l-- ) - args[l] = String.fromCharCode(97+l);//97 is 'a' - return ' ' + args.join(', ') + ' '; - }, - key:quote, //object calls it internally, the key part of an item in a map - functionCode:'[code]', //function calls it internally, it's the content of the function - attribute:quote, //node calls it internally, it's an html attribute value - string:quote, - date:quote, - regexp:literal, //regex - number:literal, - 'boolean':literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -})(); - -// from Sizzle.js -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -}; - -//from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" - */ -QUnit.diff = (function() { - function diff(o, n) { - var ns = {}; - var os = {}; - - for (var i = 0; i < n.length; i++) { - if (ns[n[i]] == null) - ns[n[i]] = { - rows: [], - o: null - }; - ns[n[i]].rows.push(i); - } - - for (var i = 0; i < o.length; i++) { - if (os[o[i]] == null) - os[o[i]] = { - rows: [], - n: null - }; - os[o[i]].rows.push(i); - } - - for (var i in ns) { - if ( !hasOwn.call( ns, i ) ) { - continue; - } - if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { - n[ns[i].rows[0]] = { - text: n[ns[i].rows[0]], - row: os[i].rows[0] - }; - o[os[i].rows[0]] = { - text: o[os[i].rows[0]], - row: ns[i].rows[0] - }; - } - } - - for (var i = 0; i < n.length - 1; i++) { - if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && - n[i + 1] == o[n[i].row + 1]) { - n[i + 1] = { - text: n[i + 1], - row: n[i].row + 1 - }; - o[n[i].row + 1] = { - text: o[n[i].row + 1], - row: i + 1 - }; - } - } - - for (var i = n.length - 1; i > 0; i--) { - if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && - n[i - 1] == o[n[i].row - 1]) { - n[i - 1] = { - text: n[i - 1], - row: n[i].row - 1 - }; - o[n[i].row - 1] = { - text: o[n[i].row - 1], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function(o, n) { - o = o.replace(/\s+$/, ''); - n = n.replace(/\s+$/, ''); - var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); - - var str = ""; - - var oSpace = o.match(/\s+/g); - if (oSpace == null) { - oSpace = [" "]; - } - else { - oSpace.push(" "); - } - var nSpace = n.match(/\s+/g); - if (nSpace == null) { - nSpace = [" "]; - } - else { - nSpace.push(" "); - } - - if (out.n.length == 0) { - for (var i = 0; i < out.o.length; i++) { - str += '<del>' + out.o[i] + oSpace[i] + "</del>"; - } - } - else { - if (out.n[0].text == null) { - for (n = 0; n < out.o.length && out.o[n].text == null; n++) { - str += '<del>' + out.o[n] + oSpace[n] + "</del>"; - } - } - - for (var i = 0; i < out.n.length; i++) { - if (out.n[i].text == null) { - str += '<ins>' + out.n[i] + nSpace[i] + "</ins>"; - } - else { - var pre = ""; - - for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { - pre += '<del>' + out.o[n] + oSpace[n] + "</del>"; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -})(); - -})(this); diff --git a/addons/web/static/lib/novajs/test/test.html b/addons/web/static/lib/novajs/test/test.html deleted file mode 100644 index 4ae40b1f59e..00000000000 --- a/addons/web/static/lib/novajs/test/test.html +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> - <script src="http://code.jquery.com/jquery-latest.js"></script> - <link rel="stylesheet" href="qunit.css" type="text/css" media="screen" /> - <script type="text/javascript" src="qunit.js"></script> - - <script type="text/javascript" src="jquery-1.6.4.js"></script> - <script type="text/javascript" src="underscore.js"></script> - <script type="text/javascript" src="../src/nova.js"></script> - - <script type="text/javascript" src="test.js"></script> - -</head> -<body> - <h1 id="qunit-header">QUnit example</h1> - <h2 id="qunit-banner"></h2> - <div id="qunit-testrunner-toolbar"></div> - <h2 id="qunit-userAgent"></h2> - <ol id="qunit-tests"></ol> - <div id="qunit-fixture">test markup, will be hidden</div> -</body> -</html>
\ No newline at end of file diff --git a/addons/web/static/lib/novajs/test/test.js b/addons/web/static/lib/novajs/test/test.js deleted file mode 100644 index 45bfed69485..00000000000 --- a/addons/web/static/lib/novajs/test/test.js +++ /dev/null @@ -1,143 +0,0 @@ - -module("Class"); - -test("base", function() { - ok(!!nova.Class, "Class does exist"); - ok(!!nova.Class.extend, "extend does exist"); - var Claz = nova.Class.extend({ - test: function() { - return "ok"; - } - }); - equal(new Claz().test(), "ok"); - var Claz2 = Claz.extend({ - test: function() { - return this._super() + "2"; - } - }); - equal(new Claz2().test(), "ok2"); -}); - -module("DestroyableMixin"); - -test("base", function() { - var Claz = nova.Class.extend(_.extend({}, nova.DestroyableMixin, {})); - var x = new Claz(); - equal(!!x.isDestroyed(), false); - x.destroy(); - equal(x.isDestroyed(), true); -}); - -module("ParentedMixin"); - -test("base", function() { - var Claz = nova.Class.extend(_.extend({}, nova.ParentedMixin, {})); - var x = new Claz(); - var y = new Claz(); - y.setParent(x); - equal(y.getParent(), x); - equal(x.getChildren()[0], y); - x.destroy(); - equal(y.isDestroyed(), true); -}); - -module("Events"); - -test("base", function() { - var x = new nova.internal.Events(); - var tmp = 0; - var fct = function() {tmp = 1;}; - x.on("test", fct); - equal(tmp, 0); - x.trigger("test"); - equal(tmp, 1); - tmp = 0; - x.off("test", fct); - x.trigger("test"); - equal(tmp, 0); -}); - -module("EventDispatcherMixin"); - -test("base", function() { - var Claz = nova.Class.extend(_.extend({}, nova.EventDispatcherMixin, {})); - var x = new Claz(); - var y = new Claz(); - var tmp = 0; - var fct = function() {tmp = 1;}; - x.on("test", y, fct); - equal(tmp, 0); - x.trigger("test"); - equal(tmp, 1); - tmp = 0; - x.off("test", y, fct); - x.trigger("test"); - equal(tmp, 0); - tmp = 0; - x.on("test", y, fct); - y.destroy(); - x.trigger("test"); - equal(tmp, 0); -}); - -module("GetterSetterMixin"); - -test("base", function() { - var Claz = nova.Class.extend(_.extend({}, nova.GetterSetterMixin, {})); - var x = new Claz(); - var y = new Claz(); - x.set({test: 1}); - equal(x.get("test"), 1); - var tmp = 0; - x.on("change:test", y, function(model, options) { - tmp = 1; - equal(options.oldValue, 1); - equal(options.newValue, 2); - equal(x.get("test"), 2); - equal(model, x); - }); - x.set({test: 2}); - equal(tmp, 1); -}); - -test("change event only when changed", function() { - var Claz = nova.Class.extend(_.extend({}, nova.GetterSetterMixin, {})); - var x = new Claz(); - var exec1 = false; - var exec2 = false; - x.on("change:test", null, function() {exec1 = true;}); - x.on("change", null, function() {exec2 = true;}); - x.set({"test": 3}); - equal(exec1, true); - equal(exec2, true); - exec1 = false; - exec2 = false; - x.set({"test": 3}); - equal(exec1, false); - equal(exec2, false); -}); - -module("Widget"); - -test("base", function() { - var Claz = nova.Widget.extend({ - renderElement: function() { - this.$element.attr("id", "testdiv"); - this.$element.html("test"); - } - }); - var x = new Claz(); - x.appendTo($("body")); - var $el = $("#testdiv"); - equal($el.length, 1); - equal($el.parents()[0], $("body")[0]); - equal($el.html(), "test"); - - var y = new Claz(x); - equal(y.getParent(), x); - - x.destroy(); - $el = $("#testdiv"); - equal($el.length, 0); -}); - diff --git a/addons/web/static/lib/novajs/test/underscore.js b/addons/web/static/lib/novajs/test/underscore.js deleted file mode 100644 index 208d4cd890c..00000000000 --- a/addons/web/static/lib/novajs/test/underscore.js +++ /dev/null @@ -1,999 +0,0 @@ -// Underscore.js 1.3.1 -// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Underscore is freely distributable under the MIT license. -// Portions of Underscore are inspired or borrowed from Prototype, -// Oliver Steele's Functional, and John Resig's Micro-Templating. -// For all details and documentation: -// http://documentcloud.github.com/underscore - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` in the browser, or `global` on the server. - var root = this; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - - // Create quick reference variables for speed access to core prototypes. - var slice = ArrayProto.slice, - unshift = ArrayProto.unshift, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var - nativeForEach = ArrayProto.forEach, - nativeMap = ArrayProto.map, - nativeReduce = ArrayProto.reduce, - nativeReduceRight = ArrayProto.reduceRight, - nativeFilter = ArrayProto.filter, - nativeEvery = ArrayProto.every, - nativeSome = ArrayProto.some, - nativeIndexOf = ArrayProto.indexOf, - nativeLastIndexOf = ArrayProto.lastIndexOf, - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { return new wrapper(obj); }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root['_'] = _; - } - - // Current version. - _.VERSION = '1.3.1'; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles objects with the built-in `forEach`, arrays, and raw objects. - // Delegates to **ECMAScript 5**'s native `forEach` if available. - var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return; - } - } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } - } - } - }; - - // Return the results of applying the iterator to each element. - // Delegates to **ECMAScript 5**'s native `map` if available. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); - each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); - }); - if (obj.length === +obj.length) results.length = obj.length; - return results; - }; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); - } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError('Reduce of empty array with no initial value'); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); - } - var reversed = _.toArray(obj).reverse(); - if (context && !initial) iterator = _.bind(iterator, context); - return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator); - }; - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; - }; - - // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. - // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - each(obj, function(value, index, list) { - if (!iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. - // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return result; - }; - - // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. - // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if a given value is included in the array or object using `===`. - // Aliased as `contains`. - _.include = _.contains = function(obj, target) { - var found = false; - if (obj == null) return found; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - found = any(obj, function(value) { - return value === target; - }); - return found; - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - return _.map(obj, function(value) { - return (_.isFunction(method) ? method || value : value[method]).apply(value, args); - }); - }; - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); - }; - - // Return the maximum element or (element-based computation). - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed >= result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Shuffle an array. - _.shuffle = function(obj) { - var shuffled = [], rand; - each(obj, function(value, index, list) { - if (index == 0) { - shuffled[0] = value; - } else { - rand = Math.floor(Math.random() * (index + 1)); - shuffled[index] = shuffled[rand]; - shuffled[rand] = value; - } - }); - return shuffled; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, iterator, context) { - return _.pluck(_.map(obj, function(value, index, list) { - return { - value : value, - criteria : iterator.call(context, value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }), 'value'); - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = function(obj, val) { - var result = {}; - var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; - each(obj, function(value, index) { - var key = iterator(value, index); - (result[key] || (result[key] = [])).push(value); - }); - return result; - }; - - // Use a comparator function to figure out at what index an object should - // be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator) { - iterator || (iterator = _.identity); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >> 1; - iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; - } - return low; - }; - - // Safely convert anything iterable into a real, live array. - _.toArray = function(iterable) { - if (!iterable) return []; - if (iterable.toArray) return iterable.toArray(); - if (_.isArray(iterable)) return slice.call(iterable); - if (_.isArguments(iterable)) return slice.call(iterable); - return _.values(iterable); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - return _.toArray(obj).length; - }; - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head`. The **guard** check allows it to work - // with `_.map`. - _.first = _.head = function(array, n, guard) { - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; - }; - - // Returns everything but the last entry of the array. Especcialy useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); - }; - - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } - }; - - // Returns everything but the first entry of the array. Aliased as `tail`. - // Especially useful on the arguments object. Passing an **index** will return - // the rest of the values in the array from that index onward. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = function(array, index, guard) { - return slice.call(array, (index == null) || guard ? 1 : index); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, function(value){ return !!value; }); - }; - - // Return a completely flattened version of an array. - _.flatten = function(array, shallow) { - return _.reduce(array, function(memo, value) { - if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value)); - memo[memo.length] = value; - return memo; - }, []); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator) { - var initial = iterator ? _.map(array, iterator) : array; - var result = []; - _.reduce(initial, function(memo, el, i) { - if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) { - memo[memo.length] = el; - result[result.length] = array[i]; - } - return memo; - }, []); - return result; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(_.flatten(arguments, true)); - }; - - // Produce an array that contains every item shared between all the - // passed-in arrays. (Aliased as "intersect" for back-compat.) - _.intersection = _.intersect = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = _.flatten(slice.call(arguments, 1)); - return _.filter(array, function(value){ return !_.include(rest, value); }); - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function() { - var args = slice.call(arguments); - var length = _.max(_.pluck(args, 'length')); - var results = new Array(length); - for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); - return results; - }; - - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i, l; - if (isSorted) { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); - for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i; - return -1; - }; - - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item) { - if (array == null) return -1; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); - var i = array.length; - while (i--) if (i in array && array[i] === item) return i; - return -1; - }; - - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; - - var len = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(len); - - while(idx < len) { - range[idx++] = start; - start += step; - } - - return range; - }; - - // Function (ahem) Functions - // ------------------ - - // Reusable constructor function for prototype setting. - var ctor = function(){}; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Binding with arguments is also known as `curry`. - // Delegates to **ECMAScript 5**'s native `Function.bind` if available. - // We check for `func.bind` first, to fail fast when `func` is undefined. - _.bind = function bind(func, context) { - var bound, args; - if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - }; - - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length == 0) funcs = _.functions(obj); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); - }; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(func, args); }, wait); - }; - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. - _.throttle = function(func, wait) { - var context, args, timeout, throttling, more; - var whenDone = _.debounce(function(){ more = throttling = false; }, wait); - return function() { - context = this; args = arguments; - var later = function() { - timeout = null; - if (more) func.apply(context, args); - whenDone(); - }; - if (!timeout) timeout = setTimeout(later, wait); - if (throttling) { - more = true; - } else { - func.apply(context, args); - } - whenDone(); - throttling = true; - }; - }; - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. - _.debounce = function(func, wait) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - func.apply(context, args); - }; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - return memo = func.apply(this, arguments); - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return function() { - var args = [func].concat(slice.call(arguments, 0)); - return wrapper.apply(this, args); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; - }; - }; - - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - if (times <= 0) return func(); - return function() { - if (--times < 1) { return func.apply(this, arguments); } - }; - }; - - // Object Functions - // ---------------- - - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - return _.map(obj, _.identity); - }; - - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); - } - return names.sort(); - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - if (obj[prop] == null) obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; - - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; - - // Internal recursive comparison function. - function eq(a, b, stack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a._chain) a = a._wrapped; - if (b._chain) b = b._wrapped; - // Invoke a custom `isEqual` method if one is provided. - if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b); - if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className != toString.call(b)) return false; - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return a == String(b); - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = stack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (stack[length] == a) return true; - } - // Add the first object to the stack of traversed objects. - stack.push(a); - var size = 0, result = true; - // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - // Ensure commutative equality for sparse arrays. - if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break; - } - } - } else { - // Objects with different constructors are not equivalent. - if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false; - // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; - } - } - // Remove the first object from the stack of traversed objects. - stack.pop(); - return result; - } - - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, []); - }; - - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; - - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType == 1); - }; - - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; - }; - - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; - - // Is a given variable an arguments object? - _.isArguments = function(obj) { - return toString.call(obj) == '[object Arguments]'; - }; - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); - }; - } - - // Is a given value a function? - _.isFunction = function(obj) { - return toString.call(obj) == '[object Function]'; - }; - - // Is a given value a string? - _.isString = function(obj) { - return toString.call(obj) == '[object String]'; - }; - - // Is a given value a number? - _.isNumber = function(obj) { - return toString.call(obj) == '[object Number]'; - }; - - // Is the given value `NaN`? - _.isNaN = function(obj) { - // `NaN` is the only value for which `===` is not reflexive. - return obj !== obj; - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; - }; - - // Is a given value a date? - _.isDate = function(obj) { - return toString.call(obj) == '[object Date]'; - }; - - // Is the given value a regular expression? - _.isRegExp = function(obj) { - return toString.call(obj) == '[object RegExp]'; - }; - - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; - - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; - - // Has own property? - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); - }; - - // Utility Functions - // ----------------- - - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; - - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; - - // Run a function **n** times. - _.times = function (n, iterator, context) { - for (var i = 0; i < n; i++) iterator.call(context, i); - }; - - // Escape a string for HTML interpolation. - _.escape = function(string) { - return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/'); - }; - - // Add your own custom functions to the Underscore object, ensuring that - // they're correctly added to the OOP wrapper as well. - _.mixin = function(obj) { - each(_.functions(obj), function(name){ - addToWrapper(name, _[name] = obj[name]); - }); - }; - - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = idCounter++; - return prefix ? prefix + id : id; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /.^/; - - // Within an interpolation, evaluation, or escaping, remove HTML escaping - // that had been previously added. - var unescape = function(code) { - return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'"); - }; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(str, data) { - var c = _.templateSettings; - var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + - 'with(obj||{}){__p.push(\'' + - str.replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(c.escape || noMatch, function(match, code) { - return "',_.escape(" + unescape(code) + "),'"; - }) - .replace(c.interpolate || noMatch, function(match, code) { - return "'," + unescape(code) + ",'"; - }) - .replace(c.evaluate || noMatch, function(match, code) { - return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('"; - }) - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t') - + "');}return __p.join('');"; - var func = new Function('obj', '_', tmpl); - if (data) return func(data, _); - return function(data) { - return func.call(this, data, _); - }; - }; - - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; - - // The OOP Wrapper - // --------------- - - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. - var wrapper = function(obj) { this._wrapped = obj; }; - - // Expose `wrapper.prototype` as `_.prototype` - _.prototype = wrapper.prototype; - - // Helper function to continue chaining intermediate results. - var result = function(obj, chain) { - return chain ? _(obj).chain() : obj; - }; - - // A method to easily add functions to the OOP wrapper. - var addToWrapper = function(name, func) { - wrapper.prototype[name] = function() { - var args = slice.call(arguments); - unshift.call(args, this._wrapped); - return result(func.apply(_, args), this._chain); - }; - }; - - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); - - // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - wrapper.prototype[name] = function() { - var wrapped = this._wrapped; - method.apply(wrapped, arguments); - var length = wrapped.length; - if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0]; - return result(wrapped, this._chain); - }; - }); - - // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - wrapper.prototype[name] = function() { - return result(method.apply(this._wrapped, arguments), this._chain); - }; - }); - - // Start chaining a wrapped Underscore object. - wrapper.prototype.chain = function() { - this._chain = true; - return this; - }; - - // Extracts the result from a wrapped and chained object. - wrapper.prototype.value = function() { - return this._wrapped; - }; - -}).call(this); diff --git a/addons/web/static/lib/py.js/.hg_archival.txt b/addons/web/static/lib/py.js/.hg_archival.txt index f5355e28a12..35e98c8501d 100644 --- a/addons/web/static/lib/py.js/.hg_archival.txt +++ b/addons/web/static/lib/py.js/.hg_archival.txt @@ -1,5 +1,5 @@ repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea -node: 5adb2d9c89e53a6445e3799f9c4dc9110458c149 +node: 1758bfec1ec1dcff95dcc4c72269cc0e3d000afd branch: default latesttag: 0.5 -latesttagdistance: 9 +latesttagdistance: 11 diff --git a/addons/web/static/lib/py.js/lib/py.js b/addons/web/static/lib/py.js/lib/py.js index 3c0b13d0717..a8c952679af 100644 --- a/addons/web/static/lib/py.js/lib/py.js +++ b/addons/web/static/lib/py.js/lib/py.js @@ -384,6 +384,21 @@ var py = {}; return new py.def(val); } + switch(val.constructor) { + case Object: + var o = new py.object(); + for (var prop in val) { + if (val.hasOwnProperty(prop)) { + o[prop] = val[prop]; + } + } + return o; + case Array: + var a = new py.list(); + a.values = val; + return a; + } + throw new Error("Could not convert " + val + " to a pyval"); } // Builtins @@ -448,7 +463,7 @@ var py = {}; __getattribute__: function (name) { if (name in this) { var val = this[name]; - if ('__get__' in val) { + if (typeof val === 'object' && '__get__' in val) { // TODO: second argument should be class return val.__get__(this); } @@ -627,6 +642,9 @@ var py = {}; } return py.False; }, + __getitem__: function (index) { + return PY_ensurepy(this.values[index.toJSON()]); + }, toJSON: function () { var out = []; for (var i=0; i<this.values.length; ++i) { @@ -846,7 +864,7 @@ var py = {}; case '[': if (expr.second) { return py.evaluate(expr.first, context) - .__getitem__(expr.evaluate(expr.second, context)); + .__getitem__(py.evaluate(expr.second, context)); } var list_exprs = expr.first, list_values = []; for (var k=0; k<list_exprs.length; ++k) { diff --git a/addons/web/static/lib/py.js/test/test.js b/addons/web/static/lib/py.js/test/test.js index 31fccc23240..c1f92c44d81 100644 --- a/addons/web/static/lib/py.js/test/test.js +++ b/addons/web/static/lib/py.js/test/test.js @@ -376,4 +376,13 @@ describe('numerical protocols', function () { }); }); }); -});
\ No newline at end of file +}); +describe('Type converter', function () { + it('should convert bare objects to objects', function () { + expect(py.eval('foo.bar', {foo: {bar: 3}})).to.be(3); + }); + it('should convert arrays to lists', function () { + expect(py.eval('foo[3]', {foo: [9, 8, 7, 6, 5]})) + .to.be(6); + }); +}); diff --git a/addons/web/static/lib/visualsearch/LICENSE b/addons/web/static/lib/visualsearch/LICENSE new file mode 100755 index 00000000000..fe0ea1154c4 --- /dev/null +++ b/addons/web/static/lib/visualsearch/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Samuel Clay, @samuelclay, DocumentCloud + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/README b/addons/web/static/lib/visualsearch/README new file mode 100755 index 00000000000..c059a9824d6 --- /dev/null +++ b/addons/web/static/lib/visualsearch/README @@ -0,0 +1,16 @@ + __ ___ _ _____ _ _ + \ \ / (_) | |/ ____| | | (_) + \ \ / / _ ___ _ _ __ _| | (___ ___ __ _ _ __ ___| |__ _ ___ + \ \/ / | / __| | | |/ _` | |\___ \ / _ \/ _` | '__/ __| '_ \ | / __| + \ / | \__ \ |_| | (_| | |____) | __/ (_| | | | (__| | | |_| \__ \ + \/ |_|___/\__,_|\__,_|_|_____/ \___|\__,_|_| \___|_| |_(_) |___/ + _/ | + |__/ + +VisualSearch.js enhances ordinary search boxes with the ability to autocomplete +faceted search queries. Specify the facets for completion, along with the +completable values for any facet. You can retrieve the search query as a +structured object, so you don't have to parse the query string yourself. + +For documentation, pre-packed downloads, demos, and tests, see: +http://documentcloud.github.com/visualsearch
\ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/Rakefile b/addons/web/static/lib/visualsearch/Rakefile new file mode 100755 index 00000000000..192a50b944e --- /dev/null +++ b/addons/web/static/lib/visualsearch/Rakefile @@ -0,0 +1,37 @@ +require 'rubygems' +require 'jammit' +require 'fileutils' + +desc "Use Jammit to compile the multiple versions of Visual Search" +task :build do + $VS_MIN = false + Jammit.package!({ + :config_path => "assets.yml", + :output_folder => "build" + }) + + $VS_MIN = true + Jammit.package!({ + :config_path => "assets.yml", + :output_folder => "build-min" + }) + + # Move the JSTs back to lib to accomodate the demo page. + FileUtils.mv("build/visualsearch_templates.js", "lib/js/templates/templates.js") + + # Fix image url paths. + ['build', 'build-min'].each do |build| + File.open("#{build}/visualsearch.css", 'r+') do |file| + css = file.read + css.gsub!(/url\((.*?)images\/embed\/icons/, 'url(../images/embed/icons') + file.rewind + file.write(css) + file.truncate(css.length) + end + end +end + +desc "Build the docco documentation" +task :docs do + sh "docco lib/js/*.js lib/js/**/*.js" +end diff --git a/addons/web/static/lib/visualsearch/assets.yml b/addons/web/static/lib/visualsearch/assets.yml new file mode 100755 index 00000000000..357c37c74aa --- /dev/null +++ b/addons/web/static/lib/visualsearch/assets.yml @@ -0,0 +1,28 @@ +embed_assets: datauri +javascript_compressor: closure +template_function: _.template +gzip_assets: <% if $VS_MIN %>on<% else %>off<% end %> +compress_assets: <% if $VS_MIN %>on<% else %>off<% end %> + +javascripts: + dependencies: + - vendor/jquery-*.js + - vendor/jquery.ui.core.js + - vendor/jquery.ui.widget.js + - vendor/jquery.ui.position.js + - vendor/jquery.ui.*.js + - vendor/underscore-*.js + - vendor/backbone-*.js + visualsearch: + - lib/js/visualsearch.js + - lib/js/views/*.js + - lib/js/utils/*.js + - lib/js/models/*.js + - lib/js/templates/*.jst + <% unless $VS_MIN %>visualsearch_templates: + - lib/js/templates/*.jst + <% end %> + +stylesheets: + visualsearch: + - lib/css/*.css
\ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css b/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css new file mode 100755 index 00000000000..3e3edd0063f --- /dev/null +++ b/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css @@ -0,0 +1,310 @@ +.VS-search .VS-icon { + background-repeat: no-repeat; + background-position: center center; + vertical-align: middle; + width: 16px; height: 16px; +} + .VS-search .VS-icon-cancel { + width: 11px; height: 11px; + background-position: center 0; + background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAWCAYAAAAW5GZjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAb9JREFUeNqUUr1qAkEQ3j0khQp6kihaeGgEEa18gTQR0iRY+BaBSMDGwidIEUKqFL6BopgqBAJ5AMFGjUU0d4WHEvwJarvZ77gRIzGYgb1hZr+Z75vZ40IIzqTNZrPj8Xicn0wmmcViEXS73aaqqq+BQODG6/W+A8MBNk3zfDAY3C6Xy0O2ZS6X6zMSiVwHg8FHLjtq7Xb7RQKj7BeTzVCgJ5PJU2U0GhUk7REuMpkMi8fjFggeMeecrVYrFRId0CgTAgDDMFg4HLbA8IjJgHNgGEr0er0fQIphUmZAwdSUADUB4RFDsz3oSMF6CLzZkQqgGebz+Z75dDqNdTqdp13bgDmdTj2VSp0oWHg0Gr2UNH2Z/9o+yMv7K4/HY/C/XhDUfr//jl7QQVT9fp/V63VWqVRYt9tliUSCZbPZg1wux9Lp9PqFeK1Wu9A0DdXz7YM87i0FrVZLs4Fi1wmFQh/NZjOmVKvVgq7rR/QflMtlixGedjwcDlUpMQ9tbzalkAAB2/R297mNW+sT2wUbUnA//V/nYrH4QOBNABUQuFQq3TNMuc82sDVrz41G42yvPeODAwZQ0QzwiJEnzLcAAwBJ6WXlwoBgZAAAAABJRU5ErkJggg=="); + cursor: pointer; + } + .VS-search .VS-icon-cancel:hover { + background-position: center -11px; + } + .VS-search .VS-icon-search { + width: 12px; height: 12px; + background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUZJREFUeNpUUM2qgmAQzS8NiUgLzTIXLZQW1QuI9AY9QPSW9gQ9QiriwpJQEBVrVWT2d7p2L9xZzDdzZs7M+YYqy/J8Ptu2vd/v4zgeDAaqqk4mE47jar9GnU6nzWbjOA5FUa/Xq0Jns9l8Pud5vkpp58cwAOzhcBhFkeu6GNztdg3D+Db5vo9nOp2iiWGYTqdDCMFe4LquI0aVpGmKR9M0lmUbjQY8YiBJklTb4YkoilBzOBzq9TogeMQIJEmqmlAlo9EIyXa7tSyrKAp4xEBkWUb5q2k8Hh+PR8/zwjCEgufz+aESstvtoKnVan2GgY31kBkEAfT1ej1FUZDiNIIgrFYr9H1ug3teLpfH43G/3/FBUJGu1+s8z8FZLpc0mmiabrfbf5fEumazuVgsTNO8Xq+3242qRNT+G0CMz7IMzH6//xZgAA60tj6rqzxpAAAAAElFTkSuQmCC"); + } + +/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/ + +/* +Eric Meyer's final reset.css +Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ +*/ +.VS-search div, .VS-search span, .VS-search a, .VS-search img, +.VS-search ul, .VS-search li, .VS-search form, .VS-search label, +.VS-interface ul, .VS-interface li, .VS-interface { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + +.VS-search :focus { + outline: 0; +} +.VS-search { + line-height: 1; + color: black; +} +.VS-search ol, .VS-search ul { + list-style: none; +} + +/* ===================== */ +/* = General and Reset = */ +/* ===================== */ + +.VS-search { + font-family: Arial, sans-serif; + color: #373737; + font-size: 12px; +} +.VS-search input { + display: block; + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + outline: none; + margin: 0; padding: 4px; + background: transparent; + font-size: 16px; + line-height: 20px; + width: 100%; +} +.VS-interface, .VS-search .dialog, .VS-search input { + font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important; + line-height: 1.1em; +} + +/* ========== */ +/* = Layout = */ +/* ========== */ + +.VS-search .VS-search-box { + cursor: text; + position: relative; + background: transparent; + border: 2px solid #ccc; + border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px; + background-color: #fafafa; + -webkit-box-shadow: inset 0px 0px 3px #ccc; + -moz-box-shadow: inset 0px 0px 3px #ccc; + box-shadow: inset 0px 0px 3px #ccc; + min-height: 28px; + height: auto; +} + .VS-search .VS-search-box.VS-focus { + border-color: #acf; + -webkit-box-shadow: inset 0px 0px 3px #acf; + -moz-box-shadow: inset 0px 0px 3px #acf; + box-shadow: inset 0px 0px 3px #acf; + } + .VS-search .VS-search-inner { + position: relative; + margin: 0 20px 0 22px; + overflow: hidden; + } + .VS-search input { + width: 100px; + } + .VS-search input, + .VS-search .VS-input-width-tester { + padding: 6px 0; + float: left; + color: #808080; + font: 13px/17px Helvetica, Arial; + } + .VS-search.VS-focus input { + color: #606060; + } + .VS-search .VS-icon-search { + position: absolute; + left: 9px; top: 8px; + } + .VS-search .VS-icon-cancel { + position: absolute; + right: 9px; top: 8px; + } + +/* ================ */ +/* = Search Facet = */ +/* ================ */ + +.VS-search .search_facet { + float: left; + margin: 0; + padding: 0 0 0 14px; + position: relative; + border: 1px solid transparent; + height: 20px; + margin: 3px -3px 3px 0; +} + .VS-search .search_facet.is_selected { + margin-left: -3px; + -webkit-border-radius: 16px; + -moz-border-radius: 16px; + border-radius: 16px; + background-color: #d2e6fd; + background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #d2e6fd, #b0d1f9); + border: 1px solid #6eadf5; + } + .VS-search .search_facet .category { + float: left; + text-transform: uppercase; + font-weight: bold; + font-size: 10px; + color: #808080; + padding: 8px 0 5px; + line-height: 13px; + cursor: pointer; + padding: 4px 0 0; + } + .VS-search .search_facet.is_selected .category { + margin-left: 3px; + } + .VS-search .search_facet .search_facet_input_container { + float: left; + } + .VS-search .search_facet input { + margin: 0; + padding: 0; + color: #000; + font-size: 13px; + line-height: 16px; + padding: 5px 0 5px 4px; + height: 16px; + width: auto; + z-index: 100; + position: relative; + padding-top: 1px; + padding-bottom: 2px; + padding-right: 3px; + + } + .VS-search .search_facet.is_editing input, + .VS-search .search_facet.is_selected input { + color: #000; + } + .VS-search .search_facet .search_facet_remove { + position: absolute; + left: 0; + top: 4px; + } + .VS-search .search_facet.is_selected .search_facet_remove { + opacity: 0.4; + left: 3px; + filter: alpha(opacity=40); + background-position: center -11px; + } + .VS-search .search_facet .search_facet_remove:hover { + opacity: 1; + } + .VS-search .search_facet.is_editing .category, + .VS-search .search_facet.is_selected .category { + color: #000; + } + .VS-search .search_facet.search_facet_maybe_delete .category, + .VS-search .search_facet.search_facet_maybe_delete input { + color: darkred; + } + +/* ================ */ +/* = Search Input = */ +/* ================ */ + +.VS-search .search_input { + height: 28px; + float: left; + margin-left: -1px; +} + .VS-search .search_input input { + padding: 6px 3px 6px 2px; + line-height: 10px; + height: 22px; + margin-top: -4px; + width: 10px; + z-index: 100; + min-width: 4px; + position: relative; + } + .VS-search .search_input.is_editing input { + color: #202020; + } + +/* ================ */ +/* = Autocomplete = */ +/* ================ */ + +.VS-interface.ui-autocomplete { + position: absolute; + border: 1px solid #C0C0C0; + border-top: 1px solid #D9D9D9; + background-color: #F6F6F6; + cursor: pointer; + z-index: 10000; + padding: 0; + margin: 0; + width: auto; + min-width: 80px; + max-width: 220px; + max-height: 240px; + overflow-y: auto; + overflow-x: hidden; + font-size: 13px; + top: 5px; + opacity: 0.97; + box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); +} + .VS-interface.ui-autocomplete .ui-autocomplete-category { + text-transform: capitalize; + font-size: 11px; + padding: 4px 4px 4px; + border-top: 1px solid #A2A2A2; + border-bottom: 1px solid #A2A2A2; + background-color: #B7B7B7; + text-shadow: 0 -1px 0 #999; + font-weight: bold; + color: white; + cursor: default; + } + .VS-interface.ui-autocomplete .ui-menu-item { + float: none; + } + .VS-interface.ui-autocomplete .ui-menu-item a { + color: #000; + outline: none; + display: block; + padding: 3px 4px 5px; + border-radius: none; + line-height: 1; + background-color: #F8F8F8; + background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #F8F8F8, #F3F3F3); + border-top: 1px solid #FAFAFA; + border-bottom: 1px solid #f0f0f0; + } + .VS-interface.ui-autocomplete .ui-menu-item a:active { + outline: none; + } + .VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover { + background-color: #6483F7; + background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #648bF5, #2465f3); + border-top: 1px solid #5b83ec; + border-bottom: 1px solid #1459e9; + border-left: none; + border-right: none; + color: white; + margin: 0; + } + .VS-interface.ui-autocomplete .ui-corner-all { + border-radius: 0; + } + .VS-interface.ui-autocomplete li { + list-style: none; + width: auto; + } diff --git a/addons/web/static/lib/visualsearch/build/visualsearch.css b/addons/web/static/lib/visualsearch/build/visualsearch.css new file mode 100755 index 00000000000..079ff122300 --- /dev/null +++ b/addons/web/static/lib/visualsearch/build/visualsearch.css @@ -0,0 +1,310 @@ +.VS-search .VS-icon { + background-repeat: no-repeat; + background-position: center center; + vertical-align: middle; + width: 16px; height: 16px; +} + .VS-search .VS-icon-cancel { + width: 11px; height: 11px; + background-position: center 0; + background-image: url(../images/embed/icons/cancel_search.png?1311104738); + cursor: pointer; + } + .VS-search .VS-icon-cancel:hover { + background-position: center -11px; + } + .VS-search .VS-icon-search { + width: 12px; height: 12px; + background-image: url(../images/embed/icons/search_glyph.png?1311104738); + } + +/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/ + +/* +Eric Meyer's final reset.css +Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ +*/ +.VS-search div, .VS-search span, .VS-search a, .VS-search img, +.VS-search ul, .VS-search li, .VS-search form, .VS-search label, +.VS-interface ul, .VS-interface li, .VS-interface { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + +.VS-search :focus { + outline: 0; +} +.VS-search { + line-height: 1; + color: black; +} +.VS-search ol, .VS-search ul { + list-style: none; +} + +/* ===================== */ +/* = General and Reset = */ +/* ===================== */ + +.VS-search { + font-family: Arial, sans-serif; + color: #373737; + font-size: 12px; +} +.VS-search input { + display: block; + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + outline: none; + margin: 0; padding: 4px; + background: transparent; + font-size: 16px; + line-height: 20px; + width: 100%; +} +.VS-interface, .VS-search .dialog, .VS-search input { + font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important; + line-height: 1.1em; +} + +/* ========== */ +/* = Layout = */ +/* ========== */ + +.VS-search .VS-search-box { + cursor: text; + position: relative; + background: transparent; + border: 2px solid #ccc; + border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px; + background-color: #fafafa; + -webkit-box-shadow: inset 0px 0px 3px #ccc; + -moz-box-shadow: inset 0px 0px 3px #ccc; + box-shadow: inset 0px 0px 3px #ccc; + min-height: 28px; + height: auto; +} + .VS-search .VS-search-box.VS-focus { + border-color: #acf; + -webkit-box-shadow: inset 0px 0px 3px #acf; + -moz-box-shadow: inset 0px 0px 3px #acf; + box-shadow: inset 0px 0px 3px #acf; + } + .VS-search .VS-search-inner { + position: relative; + margin: 0 20px 0 22px; + overflow: hidden; + } + .VS-search input { + width: 100px; + } + .VS-search input, + .VS-search .VS-input-width-tester { + padding: 6px 0; + float: left; + color: #808080; + font: 13px/17px Helvetica, Arial; + } + .VS-search.VS-focus input { + color: #606060; + } + .VS-search .VS-icon-search { + position: absolute; + left: 9px; top: 8px; + } + .VS-search .VS-icon-cancel { + position: absolute; + right: 9px; top: 8px; + } + +/* ================ */ +/* = Search Facet = */ +/* ================ */ + +.VS-search .search_facet { + float: left; + margin: 0; + padding: 0 0 0 14px; + position: relative; + border: 1px solid transparent; + height: 20px; + margin: 3px -3px 3px 0; +} + .VS-search .search_facet.is_selected { + margin-left: -3px; + -webkit-border-radius: 16px; + -moz-border-radius: 16px; + border-radius: 16px; + background-color: #d2e6fd; + background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #d2e6fd, #b0d1f9); + border: 1px solid #6eadf5; + } + .VS-search .search_facet .category { + float: left; + text-transform: uppercase; + font-weight: bold; + font-size: 10px; + color: #808080; + padding: 8px 0 5px; + line-height: 13px; + cursor: pointer; + padding: 4px 0 0; + } + .VS-search .search_facet.is_selected .category { + margin-left: 3px; + } + .VS-search .search_facet .search_facet_input_container { + float: left; + } + .VS-search .search_facet input { + margin: 0; + padding: 0; + color: #000; + font-size: 13px; + line-height: 16px; + padding: 5px 0 5px 4px; + height: 16px; + width: auto; + z-index: 100; + position: relative; + padding-top: 1px; + padding-bottom: 2px; + padding-right: 3px; + + } + .VS-search .search_facet.is_editing input, + .VS-search .search_facet.is_selected input { + color: #000; + } + .VS-search .search_facet .search_facet_remove { + position: absolute; + left: 0; + top: 4px; + } + .VS-search .search_facet.is_selected .search_facet_remove { + opacity: 0.4; + left: 3px; + filter: alpha(opacity=40); + background-position: center -11px; + } + .VS-search .search_facet .search_facet_remove:hover { + opacity: 1; + } + .VS-search .search_facet.is_editing .category, + .VS-search .search_facet.is_selected .category { + color: #000; + } + .VS-search .search_facet.search_facet_maybe_delete .category, + .VS-search .search_facet.search_facet_maybe_delete input { + color: darkred; + } + +/* ================ */ +/* = Search Input = */ +/* ================ */ + +.VS-search .search_input { + height: 28px; + float: left; + margin-left: -1px; +} + .VS-search .search_input input { + padding: 6px 3px 6px 2px; + line-height: 10px; + height: 22px; + margin-top: -4px; + width: 10px; + z-index: 100; + min-width: 4px; + position: relative; + } + .VS-search .search_input.is_editing input { + color: #202020; + } + +/* ================ */ +/* = Autocomplete = */ +/* ================ */ + +.VS-interface.ui-autocomplete { + position: absolute; + border: 1px solid #C0C0C0; + border-top: 1px solid #D9D9D9; + background-color: #F6F6F6; + cursor: pointer; + z-index: 10000; + padding: 0; + margin: 0; + width: auto; + min-width: 80px; + max-width: 220px; + max-height: 240px; + overflow-y: auto; + overflow-x: hidden; + font-size: 13px; + top: 5px; + opacity: 0.97; + box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); +} + .VS-interface.ui-autocomplete .ui-autocomplete-category { + text-transform: capitalize; + font-size: 11px; + padding: 4px 4px 4px; + border-top: 1px solid #A2A2A2; + border-bottom: 1px solid #A2A2A2; + background-color: #B7B7B7; + text-shadow: 0 -1px 0 #999; + font-weight: bold; + color: white; + cursor: default; + } + .VS-interface.ui-autocomplete .ui-menu-item { + float: none; + } + .VS-interface.ui-autocomplete .ui-menu-item a { + color: #000; + outline: none; + display: block; + padding: 3px 4px 5px; + border-radius: none; + line-height: 1; + background-color: #F8F8F8; + background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #F8F8F8, #F3F3F3); + border-top: 1px solid #FAFAFA; + border-bottom: 1px solid #f0f0f0; + } + .VS-interface.ui-autocomplete .ui-menu-item a:active { + outline: none; + } + .VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover { + background-color: #6483F7; + background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */ + background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */ + background-image: linear-gradient(top, #648bF5, #2465f3); + border-top: 1px solid #5b83ec; + border-bottom: 1px solid #1459e9; + border-left: none; + border-right: none; + color: white; + margin: 0; + } + .VS-interface.ui-autocomplete .ui-corner-all { + border-radius: 0; + } + .VS-interface.ui-autocomplete li { + list-style: none; + width: auto; + } diff --git a/addons/web/static/lib/visualsearch/build/visualsearch.js b/addons/web/static/lib/visualsearch/build/visualsearch.js new file mode 100755 index 00000000000..319d978db3c --- /dev/null +++ b/addons/web/static/lib/visualsearch/build/visualsearch.js @@ -0,0 +1,1845 @@ +// This is the annotated source code for +// [VisualSearch.js](http://documentcloud.github.com/visualsearch/), +// a rich search box for real data. +// +// The annotated source HTML is generated by +// [Docco](http://jashkenas.github.com/docco/). + +/** @license VisualSearch.js 0.2.2 + * (c) 2011 Samuel Clay, @samuelclay, DocumentCloud Inc. + * VisualSearch.js may be freely distributed under the MIT license. + * For all details and documentation: + * http://documentcloud.github.com/visualsearch + */ + +(function() { + + var $ = jQuery; // Handle namespaced jQuery + + // Setting up VisualSearch globals. These will eventually be made instance-based. + if (!window.VS) window.VS = {}; + if (!VS.app) VS.app = {}; + if (!VS.ui) VS.ui = {}; + if (!VS.model) VS.model = {}; + if (!VS.utils) VS.utils = {}; + + // Sets the version for VisualSearch to be used programatically elsewhere. + VS.VERSION = '0.2.2'; + + VS.VisualSearch = function(options) { + var defaults = { + container : '', + query : '', + unquotable : [], + callbacks : { + search : $.noop, + focus : $.noop, + blur : $.noop, + facetMatches : $.noop, + valueMatches : $.noop + } + }; + this.options = _.extend({}, defaults, options); + this.options.callbacks = _.extend({}, defaults.callbacks, options.callbacks); + + VS.app.hotkeys.initialize(); + this.searchQuery = new VS.model.SearchQuery(); + this.searchBox = new VS.ui.SearchBox({app: this}); + + if (options.container) { + var searchBox = this.searchBox.render().el; + $(this.options.container).html(searchBox); + } + this.searchBox.value(this.options.query || ''); + + // Disable page caching for browsers that incorrectly cache the visual search inputs. + // This is forced the browser to re-render the page when it is retrieved in its history. + $(window).bind('unload', function(e) {}); + + // Gives the user back a reference to the `searchBox` so they + // can use public methods. + return this; + }; + + // Entry-point used to tie all parts of VisualSearch together. It will either attach + // itself to `options.container`, or pass back the `searchBox` so it can be rendered + // at will. + VS.init = function(options) { + return new VS.VisualSearch(options); + }; + +})(); +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// The search box is responsible for managing the many facet views and input views. +VS.ui.SearchBox = Backbone.View.extend({ + + id : 'search', + + events : { + 'click .VS-cancel-search-box' : 'clearSearch', + 'mousedown .VS-search-box' : 'maybeFocusSearch', + 'dblclick .VS-search-box' : 'highlightSearch', + 'click .VS-search-box' : 'maybeTripleClick' + }, + + // Creating a new SearchBox registers handlers for re-rendering facets when necessary, + // as well as handling typing when a facet is selected. + initialize : function() { + this.app = this.options.app; + this.flags = { + allSelected : false + }; + this.facetViews = []; + this.inputViews = []; + _.bindAll(this, 'renderFacets', '_maybeDisableFacets', 'disableFacets', + 'deselectAllFacets', 'addedFacet', 'removedFacet', 'changedFacet'); + this.app.searchQuery + .bind('reset', this.renderFacets) + .bind('add', this.addedFacet) + .bind('remove', this.removedFacet) + .bind('change', this.changedFacet); + $(document).bind('keydown', this._maybeDisableFacets); + }, + + // Renders the search box, but requires placement on the page through `this.el`. + render : function() { + $(this.el).append(JST['search_box']({})); + $(document.body).setMode('no', 'search'); + + return this; + }, + + // # Querying Facets # + + // Either gets a serialized query string or sets the faceted query from a query string. + value : function(query) { + if (query == null) return this.serialize(); + return this.setQuery(query); + }, + + // Uses the VS.app.searchQuery collection to serialize the current query from the various + // facets that are in the search box. + serialize : function() { + var query = []; + var inputViewsCount = this.inputViews.length; + + this.app.searchQuery.each(_.bind(function(facet, i) { + query.push(this.inputViews[i].value()); + query.push(facet.serialize()); + }, this)); + + if (inputViewsCount) { + query.push(this.inputViews[inputViewsCount-1].value()); + } + + return _.compact(query).join(' '); + }, + + // Takes a query string and uses the SearchParser to parse and render it. Note that + // `VS.app.SearchParser` refreshes the `VS.app.searchQuery` collection, which is bound + // here to call `this.renderFacets`. + setQuery : function(query) { + this.currentQuery = query; + VS.app.SearchParser.parse(this.app, query); + }, + + // Returns the position of a facet/input view. Useful when moving between facets. + viewPosition : function(view) { + var views = view.type == 'facet' ? this.facetViews : this.inputViews; + var position = _.indexOf(views, view); + if (position == -1) position = 0; + return position; + }, + + // Used to launch a search. Hitting enter or clicking the search button. + searchEvent : function(e) { + var query = this.value(); + this.focusSearch(e); + this.value(query); + this.app.options.callbacks.search(query, this.app.searchQuery); + }, + + // # Rendering Facets # + + // Add a new facet. Facet will be focused and ready to accept a value. Can also + // specify position, in the case of adding facets from an inbetween input. + addFacet : function(category, initialQuery, position) { + category = VS.utils.inflector.trim(category); + initialQuery = VS.utils.inflector.trim(initialQuery || ''); + if (!category) return; + + var model = new VS.model.SearchFacet({ + category : category, + value : initialQuery || '', + app : this.app + }); + this.app.searchQuery.add(model, {at: position}); + }, + + // Renders a newly added facet, and selects it. + addedFacet : function (model) { + this.renderFacets(); + var facetView = _.detect(this.facetViews, function(view) { + if (view.model == model) return true; + }); + + _.defer(function() { + facetView.enableEdit(); + }); + }, + + // Changing a facet programmatically re-renders it. + changedFacet: function () { + this.renderFacets(); + }, + + // When removing a facet, potentially do something. For now, the adjacent + // remaining facet is selected, but this is handled by the facet's view, + // since its position is unknown by the time the collection triggers this + // remove callback. + removedFacet : function (facet, query, options) {}, + + // Renders each facet as a searchFacet view. + renderFacets : function() { + this.facetViews = []; + this.inputViews = []; + + this.$('.VS-search-inner').empty(); + + this.app.searchQuery.each(_.bind(function(facet, i) { + this.renderFacet(facet, i); + }, this)); + + // Add on an n+1 empty search input on the very end. + this.renderSearchInput(); + }, + + // Render a single facet, using its category and query value. + renderFacet : function(facet, position) { + var view = new VS.ui.SearchFacet({ + app : this.app, + model : facet, + order : position + }); + + // Input first, facet second. + this.renderSearchInput(); + this.facetViews.push(view); + this.$('.VS-search-inner').children().eq(position*2).after(view.render().el); + + view.calculateSize(); + _.defer(_.bind(view.calculateSize, view)); + + return view; + }, + + // Render a single input, used to create and autocomplete facets + renderSearchInput : function() { + var input = new VS.ui.SearchInput({position: this.inputViews.length, app: this.app}); + this.$('.VS-search-inner').append(input.render().el); + this.inputViews.push(input); + }, + + // # Modifying Facets # + + // Clears out the search box. Command+A + delete can trigger this, as can a cancel button. + // + // If a `clearSearch` callback was provided, the callback is invoked and + // provided with a function performs the actual removal of the data. This + // allows third-party developers to either clear data asynchronously, or + // prior to performing their custom "clear" logic. + clearSearch : function(e) { + var actualClearSearch = _.bind(function() { + this.disableFacets(); + this.value(''); + this.flags.allSelected = false; + this.searchEvent(e); + this.focusSearch(e); + }, this); + + if (this.app.options.callbacks.clearSearch) { + this.app.options.callbacks.clearSearch(actualClearSearch); + } else { + actualClearSearch(); + } + }, + + // Command+A selects all facets. + selectAllFacets : function() { + this.flags.allSelected = true; + + $(document).one('click.selectAllFacets', this.deselectAllFacets); + + _.each(this.facetViews, function(facetView, i) { + facetView.selectFacet(); + }); + _.each(this.inputViews, function(inputView, i) { + inputView.selectText(); + }); + }, + + // Used by facets and input to see if all facets are currently selected. + allSelected : function(deselect) { + if (deselect) this.flags.allSelected = false; + return this.flags.allSelected; + }, + + // After `selectAllFacets` is engaged, this method is bound to the entire document. + // This immediate disables and deselects all facets, but it also checks if the user + // has clicked on either a facet or an input, and properly selects the view. + deselectAllFacets : function(e) { + this.disableFacets(); + + if (this.$(e.target).is('.category,input')) { + var el = $(e.target).closest('.search_facet,.search_input'); + var view = _.detect(this.facetViews.concat(this.inputViews), function(v) { + return v.el == el[0]; + }); + if (view.type == 'facet') { + view.selectFacet(); + } else if (view.type == 'input') { + _.defer(function() { + view.enableEdit(true); + }); + } + } + }, + + // Disables all facets except for the passed in view. Used when switching between + // facets, so as not to have to keep state of active facets. + disableFacets : function(keepView) { + _.each(this.inputViews, function(view) { + if (view && view != keepView && + (view.modes.editing == 'is' || view.modes.selected == 'is')) { + view.disableEdit(); + } + }); + _.each(this.facetViews, function(view) { + if (view && view != keepView && + (view.modes.editing == 'is' || view.modes.selected == 'is')) { + view.disableEdit(); + view.deselectFacet(); + } + }); + + this.flags.allSelected = false; + this.removeFocus(); + $(document).unbind('click.selectAllFacets'); + }, + + // Resize all inputs to account for extra keystrokes which may be changing the facet + // width incorrectly. This is a safety check to ensure inputs are correctly sized. + resizeFacets : function(view) { + _.each(this.facetViews, function(facetView, i) { + if (!view || facetView == view) { + facetView.resize(); + } + }); + }, + + // Handles keydown events on the document. Used to complete the Cmd+A deletion, and + // blurring focus. + _maybeDisableFacets : function(e) { + if (this.flags.allSelected && VS.app.hotkeys.key(e) == 'backspace') { + e.preventDefault(); + this.clearSearch(e); + return false; + } else if (this.flags.allSelected && VS.app.hotkeys.printable(e)) { + this.clearSearch(e); + } + }, + + // # Focusing Facets # + + // Move focus between facets and inputs. Takes a direction as well as many options + // for skipping over inputs and only to facets, placement of cursor position in facet + // (i.e. at the end), and selecting the text in the input/facet. + focusNextFacet : function(currentView, direction, options) { + options = options || {}; + var viewCount = this.facetViews.length; + var viewPosition = options.viewPosition || this.viewPosition(currentView); + + if (!options.skipToFacet) { + // Correct for bouncing between matching text and facet arrays. + if (currentView.type == 'text' && direction > 0) direction -= 1; + if (currentView.type == 'facet' && direction < 0) direction += 1; + } else if (options.skipToFacet && currentView.type == 'text' && + viewCount == viewPosition && direction >= 0) { + // Special case of looping around to a facet from the last search input box. + viewPosition = 0; + direction = 0; + } + var view, next = Math.min(viewCount, viewPosition + direction); + + if (currentView.type == 'text') { + if (next >= 0 && next < viewCount) { + view = this.facetViews[next]; + } else if (next == viewCount) { + view = this.inputViews[this.inputViews.length-1]; + } + if (view && options.selectFacet && view.type == 'facet') { + view.selectFacet(); + } else if (view) { + view.enableEdit(); + view.setCursorAtEnd(direction || options.startAtEnd); + } + } else if (currentView.type == 'facet') { + if (options.skipToFacet) { + if (next >= viewCount || next < 0) { + view = _.last(this.inputViews); + view.enableEdit(); + } else { + view = this.facetViews[next]; + view.enableEdit(); + view.setCursorAtEnd(direction || options.startAtEnd); + } + } else { + view = this.inputViews[next]; + view.enableEdit(); + } + } + if (options.selectText) view.selectText(); + this.resizeFacets(); + }, + + maybeFocusSearch : function(e) { + if ($(e.target).is('.VS-search-box') || + $(e.target).is('.VS-search-inner') || + e.type == 'keydown') { + this.focusSearch(e); + } + }, + + // Bring focus to last input field. + focusSearch : function(e, selectText) { + var view = this.inputViews[this.inputViews.length-1]; + view.enableEdit(selectText); + if (!selectText) view.setCursorAtEnd(-1); + if (e.type == 'keydown') { + view.keydown(e); + view.box.trigger('keydown'); + } + _.defer(_.bind(function() { + if (!this.$('input:focus').length) { + view.enableEdit(selectText); + } + }, this)); + }, + + // Double-clicking on the search wrapper should select the existing text in + // the last search input. Also start the triple-click timer. + highlightSearch : function(e) { + if ($(e.target).is('.VS-search-box') || + $(e.target).is('.VS-search-inner') || + e.type == 'keydown') { + var lastinput = this.inputViews[this.inputViews.length-1]; + lastinput.startTripleClickTimer(); + this.focusSearch(e, true); + } + }, + + maybeTripleClick : function(e) { + var lastinput = this.inputViews[this.inputViews.length-1]; + return lastinput.maybeTripleClick(e); + }, + + // Used to show the user is focused on some input inside the search box. + addFocus : function() { + this.app.options.callbacks.focus(); + this.$('.VS-search-box').addClass('VS-focus'); + }, + + // User is no longer focused on anything in the search box. + removeFocus : function() { + this.app.options.callbacks.blur(); + var focus = _.any(this.facetViews.concat(this.inputViews), function(view) { + return view.isFocused(); + }); + if (!focus) this.$('.VS-search-box').removeClass('VS-focus'); + }, + + // Show a menu which adds pre-defined facets to the search box. This is unused for now. + showFacetCategoryMenu : function(e) { + e.preventDefault(); + e.stopPropagation(); + if (this.facetCategoryMenu && this.facetCategoryMenu.modes.open == 'is') { + return this.facetCategoryMenu.close(); + } + + var items = [ + {title: 'Account', onClick: _.bind(this.addFacet, this, 'account', '')}, + {title: 'Project', onClick: _.bind(this.addFacet, this, 'project', '')}, + {title: 'Filter', onClick: _.bind(this.addFacet, this, 'filter', '')}, + {title: 'Access', onClick: _.bind(this.addFacet, this, 'access', '')} + ]; + + var menu = this.facetCategoryMenu || (this.facetCategoryMenu = new dc.ui.Menu({ + items : items, + standalone : true + })); + + this.$('.VS-icon-search').after(menu.render().open().content); + return false; + } + +}); + +})(); + +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// This is the visual search facet that holds the category and its autocompleted +// input field. +VS.ui.SearchFacet = Backbone.View.extend({ + + type : 'facet', + + className : 'search_facet', + + events : { + 'click .category' : 'selectFacet', + 'keydown input' : 'keydown', + 'mousedown input' : 'enableEdit', + 'mouseover .VS-icon-cancel' : 'showDelete', + 'mouseout .VS-icon-cancel' : 'hideDelete', + 'click .VS-icon-cancel' : 'remove' + }, + + initialize : function(options) { + this.flags = { + canClose : false + }; + _.bindAll(this, 'set', 'keydown', 'deselectFacet', 'deferDisableEdit'); + }, + + // Rendering the facet sets up autocompletion, events on blur, and populates + // the facet's input with its starting value. + render : function() { + $(this.el).html(JST['search_facet']({ + model : this.model + })); + + this.setMode('not', 'editing'); + this.setMode('not', 'selected'); + this.box = this.$('input'); + this.box.val(this.model.get('value')); + this.box.bind('blur', this.deferDisableEdit); + // Handle paste events with `propertychange` + this.box.bind('input propertychange', this.keydown); + this.setupAutocomplete(); + + return this; + }, + + // This method is used to setup the facet's input to auto-grow. + // This is defered in the searchBox so it can be attached to the + // DOM to get the correct font-size. + calculateSize : function() { + this.box.autoGrowInput(); + this.box.unbind('updated.autogrow'); + this.box.bind('updated.autogrow', _.bind(this.moveAutocomplete, this)); + }, + + // Forces a recalculation of this facet's input field's value. Called when + // the facet is focused, removed, or otherwise modified. + resize : function(e) { + this.box.trigger('resize.autogrow', e); + }, + + // Watches the facet's input field to see if it matches the beginnings of + // words in `autocompleteValues`, which is different for every category. + // If the value, when selected from the autocompletion menu, is different + // than what it was, commit the facet and search for it. + setupAutocomplete : function() { + this.box.autocomplete({ + source : _.bind(this.autocompleteValues, this), + minLength : 0, + delay : 0, + autoFocus : true, + position : {offset : "0 5"}, + create : _.bind(function(e, ui) { + $(this.el).find('.ui-autocomplete-input').css('z-index','auto'); + }, this), + select : _.bind(function(e, ui) { + e.preventDefault(); + var originalValue = this.model.get('value'); + this.set(ui.item.value); + if (originalValue != ui.item.value || this.box.val() != ui.item.value) { + this.search(e); + } + return false; + }, this), + open : _.bind(function(e, ui) { + var box = this.box; + this.box.autocomplete('widget').find('.ui-menu-item').each(function() { + var $value = $(this); + if ($value.data('item.autocomplete')['value'] == box.val()) { + box.data('autocomplete').menu.activate(new $.Event("mouseover"), $value); + } + }); + }, this) + }); + + this.box.autocomplete('widget').addClass('VS-interface'); + }, + + // As the facet's input field grows, it may move to the next line in the + // search box. `autoGrowInput` triggers an `updated` event on the input + // field, which is bound to this method to move the autocomplete menu. + moveAutocomplete : function() { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) { + autocomplete.menu.element.position({ + my : "left top", + at : "left bottom", + of : this.box.data('autocomplete').element, + collision : "flip", + offset : "0 5" + }); + } + }, + + // When a user enters a facet and it is being edited, immediately show + // the autocomplete menu and size it to match the contents. + searchAutocomplete : function(e) { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) { + var menu = autocomplete.menu.element; + autocomplete.search(); + + // Resize the menu based on the correctly measured width of what's bigger: + // the menu's original size or the menu items' new size. + menu.outerWidth(Math.max( + menu.width('').outerWidth(), + autocomplete.element.outerWidth() + )); + } + }, + + // Closes the autocomplete menu. Called on disabling, selecting, deselecting, + // and anything else that takes focus out of the facet's input field. + closeAutocomplete : function() { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) autocomplete.close(); + }, + + // Search terms used in the autocomplete menu. These are specific to the facet, + // and only match for the facet's category. The values are then matched on the + // first letter of any word in matches, and finally sorted according to the + // value's own category. You can pass `preserveOrder` as an option in the + // `facetMatches` callback to skip any further ordering done client-side. + autocompleteValues : function(req, resp) { + var category = this.model.get('category'); + var value = this.model.get('value'); + var searchTerm = req.term; + + this.options.app.options.callbacks.valueMatches(category, searchTerm, function(matches, options) { + options = options || {}; + matches = matches || []; + + if (searchTerm && value != searchTerm) { + if (options.preserveMatches) { + return matches; + } else { + var re = VS.utils.inflector.escapeRegExp(searchTerm || ''); + var matcher = new RegExp('\\b' + re, 'i'); + matches = $.grep(matches, function(item) { + return matcher.test(item) || + matcher.test(item.value) || + matcher.test(item.label); + }); + } + } + + if (options.preserveOrder) { + resp(matches); + } else { + resp(_.sortBy(matches, function(match) { + if (match == value || match.value == value) return ''; + else return match; + })); + } + }); + + }, + + // Sets the facet's model's value. + set : function(value) { + if (!value) return; + this.model.set({'value': value}); + }, + + // Before the searchBox performs a search, we need to close the + // autocomplete menu. + search : function(e, direction) { + if (!direction) direction = 1; + this.closeAutocomplete(); + this.options.app.searchBox.searchEvent(e); + _.defer(_.bind(function() { + this.options.app.searchBox.focusNextFacet(this, direction, {viewPosition: this.options.order}); + }, this)); + }, + + // Begin editing the facet's input. This is called when the user enters + // the input either from another facet or directly clicking on it. + // + // This method tells all other facets and inputs to disable so it can have + // the sole focus. It also prepares the autocompletion menu. + enableEdit : function() { + if (this.modes.editing != 'is') { + this.setMode('is', 'editing'); + this.deselectFacet(); + if (this.box.val() == '') { + this.box.val(this.model.get('value')); + } + } + + this.flags.canClose = false; + this.options.app.searchBox.disableFacets(this); + this.options.app.searchBox.addFocus(); + _.defer(_.bind(function() { + this.options.app.searchBox.addFocus(); + }, this)); + this.resize(); + this.searchAutocomplete(); + this.box.focus(); + }, + + // When the user blurs the input, they may either be going to another input + // or off the search box entirely. If they go to another input, this facet + // will be instantly disabled, and the canClose flag will be turned back off. + // + // However, if the user clicks elsewhere on the page, this method starts a timer + // that checks if any of the other inputs are selected or are being edited. If + // not, then it can finally close itself and its autocomplete menu. + deferDisableEdit : function() { + this.flags.canClose = true; + _.delay(_.bind(function() { + if (this.flags.canClose && !this.box.is(':focus') && + this.modes.editing == 'is' && this.modes.selected != 'is') { + this.disableEdit(); + } + }, this), 250); + }, + + // Called either by other facets receiving focus or by the timer in `deferDisableEdit`, + // this method will turn off the facet, remove any text selection, and close + // the autocomplete menu. + disableEdit : function() { + var newFacetQuery = VS.utils.inflector.trim(this.box.val()); + if (newFacetQuery != this.model.get('value')) { + this.set(newFacetQuery); + } + this.flags.canClose = false; + this.box.selectRange(0, 0); + this.box.blur(); + this.setMode('not', 'editing'); + this.closeAutocomplete(); + this.options.app.searchBox.removeFocus(); + }, + + // Selects the facet, which blurs the facet's input and highlights the facet. + // If this is the only facet being selected (and not part of a select all event), + // we attach a mouse/keyboard watcher to check if the next action by the user + // should delete this facet or just deselect it. + selectFacet : function(e) { + if (e) e.preventDefault(); + var allSelected = this.options.app.searchBox.allSelected(); + if (this.modes.selected == 'is') return; + + if (this.box.is(':focus')) { + this.box.setCursorPosition(0); + this.box.blur(); + } + + this.flags.canClose = false; + this.closeAutocomplete(); + this.setMode('is', 'selected'); + this.setMode('not', 'editing'); + if (!allSelected || e) { + $(document).unbind('keydown.facet', this.keydown); + $(document).unbind('click.facet', this.deselectFacet); + _.defer(_.bind(function() { + $(document).unbind('keydown.facet').bind('keydown.facet', this.keydown); + $(document).unbind('click.facet').one('click.facet', this.deselectFacet); + }, this)); + this.options.app.searchBox.disableFacets(this); + this.options.app.searchBox.addFocus(); + } + return false; + }, + + // Turns off highlighting on the facet. Called in a variety of ways, this + // only deselects the facet if it is selected, and then cleans up the + // keyboard/mouse watchers that were created when the facet was first + // selected. + deselectFacet : function(e) { + if (e) e.preventDefault(); + if (this.modes.selected == 'is') { + this.setMode('not', 'selected'); + this.closeAutocomplete(); + this.options.app.searchBox.removeFocus(); + } + $(document).unbind('keydown.facet', this.keydown); + $(document).unbind('click.facet', this.deselectFacet); + return false; + }, + + // Is the user currently focused in this facet's input field? + isFocused : function() { + return this.box.is(':focus'); + }, + + // Hovering over the delete button styles the facet so the user knows that + // the delete button will kill the entire facet. + showDelete : function() { + $(this.el).addClass('search_facet_maybe_delete'); + }, + + // On `mouseout`, the user is no longer hovering on the delete button. + hideDelete : function() { + $(this.el).removeClass('search_facet_maybe_delete'); + }, + + // When switching between facets, depending on the direction the cursor is + // coming from, the cursor in this facet's input field should match the original + // direction. + setCursorAtEnd : function(direction) { + if (direction == -1) { + this.box.setCursorPosition(this.box.val().length); + } else { + this.box.setCursorPosition(0); + } + }, + + // Deletes the facet and sends the cursor over to the nearest input field. + remove : function(e) { + var committed = this.model.get('value'); + this.deselectFacet(); + this.disableEdit(); + this.options.app.searchQuery.remove(this.model); + if (committed) { + this.search(e, -1); + } else { + this.options.app.searchBox.renderFacets(); + this.options.app.searchBox.focusNextFacet(this, -1, {viewPosition: this.options.order}); + } + }, + + // Selects the text in the facet's input field. When the user tabs between + // facets, convention is to highlight the entire field. + selectText: function() { + this.box.selectRange(0, this.box.val().length); + }, + + // Handles all keyboard inputs when in the facet's input field. This checks + // for movement between facets and inputs, entering a new value that needs + // to be autocompleted, as well as the removal of this facet. + keydown : function(e) { + var key = VS.app.hotkeys.key(e); + + if (key == 'enter' && this.box.val()) { + this.disableEdit(); + this.search(e); + } else if (key == 'left') { + if (this.modes.selected == 'is') { + this.deselectFacet(); + this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1}); + } else if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) { + this.selectFacet(); + } + } else if (key == 'right') { + if (this.modes.selected == 'is') { + e.preventDefault(); + this.deselectFacet(); + this.setCursorAtEnd(0); + this.enableEdit(); + } else if (this.box.getCursorPosition() == this.box.val().length) { + e.preventDefault(); + this.disableEdit(); + this.options.app.searchBox.focusNextFacet(this, 1); + } + } else if (VS.app.hotkeys.shift && key == 'tab') { + e.preventDefault(); + this.options.app.searchBox.focusNextFacet(this, -1, { + startAtEnd : -1, + skipToFacet : true, + selectText : true + }); + } else if (key == 'tab') { + e.preventDefault(); + this.options.app.searchBox.focusNextFacet(this, 1, { + skipToFacet : true, + selectText : true + }); + } else if (VS.app.hotkeys.command && (e.which == 97 || e.which == 65)) { + e.preventDefault(); + this.options.app.searchBox.selectAllFacets(); + return false; + } else if (VS.app.hotkeys.printable(e) && this.modes.selected == 'is') { + this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1}); + this.remove(e); + } else if (key == 'backspace') { + if (this.modes.selected == 'is') { + e.preventDefault(); + this.remove(e); + } else if (this.box.getCursorPosition() == 0 && + !this.box.getSelection().length) { + e.preventDefault(); + this.selectFacet(); + } + } + + this.resize(e); + + // Handle paste events + if (e.which == null) { + this.searchAutocomplete(e); + _.defer(_.bind(this.resize, this, e)); + } + } + +}); + +})(); + +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// This is the visual search input that is responsible for creating new facets. +// There is one input placed in between all facets. +VS.ui.SearchInput = Backbone.View.extend({ + + type : 'text', + + className : 'search_input', + + events : { + 'keypress input' : 'keypress', + 'keydown input' : 'keydown', + 'click input' : 'maybeTripleClick', + 'dblclick input' : 'startTripleClickTimer' + }, + + initialize : function() { + this.app = this.options.app; + this.flags = { + canClose : false + }; + _.bindAll(this, 'removeFocus', 'addFocus', 'moveAutocomplete', 'deferDisableEdit'); + }, + + // Rendering the input sets up autocomplete, events on focusing and blurring + // the input, and the auto-grow of the input. + render : function() { + $(this.el).html(JST['search_input']({})); + + this.setMode('not', 'editing'); + this.setMode('not', 'selected'); + this.box = this.$('input'); + this.box.autoGrowInput(); + this.box.bind('updated.autogrow', this.moveAutocomplete); + this.box.bind('blur', this.deferDisableEdit); + this.box.bind('focus', this.addFocus); + this.setupAutocomplete(); + + return this; + }, + + // Watches the input and presents an autocompleted menu, taking the + // remainder of the input field and adding a separate facet for it. + // + // See `addTextFacetRemainder` for explanation on how the remainder works. + setupAutocomplete : function() { + this.box.autocomplete({ + minLength : 1, + delay : 50, + autoFocus : true, + position : {offset : "0 -1"}, + source : _.bind(this.autocompleteValues, this), + create : _.bind(function(e, ui) { + $(this.el).find('.ui-autocomplete-input').css('z-index','auto'); + }, this), + select : _.bind(function(e, ui) { + e.preventDefault(); + e.stopPropagation(); + var remainder = this.addTextFacetRemainder(ui.item.value); + var position = this.options.position + (remainder ? 1 : 0); + this.app.searchBox.addFacet(ui.item.value, '', position); + return false; + }, this) + }); + + // Renders the results grouped by the categories they belong to. + this.box.data('autocomplete')._renderMenu = function(ul, items) { + var category = ''; + _.each(items, _.bind(function(item, i) { + if (item.category && item.category != category) { + ul.append('<li class="ui-autocomplete-category">'+item.category+'</li>'); + category = item.category; + } + this._renderItem(ul, item); + }, this)); + }; + + this.box.autocomplete('widget').addClass('VS-interface'); + }, + + // Search terms used in the autocomplete menu. The values are matched on the + // first letter of any word in matches, and finally sorted according to the + // value's own category. You can pass `preserveOrder` as an option in the + // `facetMatches` callback to skip any further ordering done client-side. + autocompleteValues : function(req, resp) { + var searchTerm = req.term; + var lastWord = searchTerm.match(/\w+$/); // Autocomplete only last word. + var re = VS.utils.inflector.escapeRegExp(lastWord && lastWord[0] || ' '); + this.app.options.callbacks.facetMatches(function(prefixes, options) { + options = options || {}; + prefixes = prefixes || []; + + // Only match from the beginning of the word. + var matcher = new RegExp('^' + re, 'i'); + var matches = $.grep(prefixes, function(item) { + return item && matcher.test(item.label || item); + }); + + if (options.preserveOrder) { + resp(matches); + } else { + resp(_.sortBy(matches, function(match) { + if (match.label) return match.category + '-' + match.label; + else return match; + })); + } + }); + + }, + + // Closes the autocomplete menu. Called on disabling, selecting, deselecting, + // and anything else that takes focus out of the facet's input field. + closeAutocomplete : function() { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) autocomplete.close(); + }, + + // As the input field grows, it may move to the next line in the + // search box. `autoGrowInput` triggers an `updated` event on the input + // field, which is bound to this method to move the autocomplete menu. + moveAutocomplete : function() { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) { + autocomplete.menu.element.position({ + my : "left top", + at : "left bottom", + of : this.box.data('autocomplete').element, + collision : "none", + offset : '0 -1' + }); + } + }, + + // When a user enters a facet and it is being edited, immediately show + // the autocomplete menu and size it to match the contents. + searchAutocomplete : function(e) { + var autocomplete = this.box.data('autocomplete'); + if (autocomplete) { + var menu = autocomplete.menu.element; + autocomplete.search(); + + // Resize the menu based on the correctly measured width of what's bigger: + // the menu's original size or the menu items' new size. + menu.outerWidth(Math.max( + menu.width('').outerWidth(), + autocomplete.element.outerWidth() + )); + } + }, + + // If a user searches for "word word category", the category would be + // matched and autocompleted, and when selected, the "word word" would + // also be caught as the remainder and then added in its own facet. + addTextFacetRemainder : function(facetValue) { + var boxValue = this.box.val(); + var lastWord = boxValue.match(/\b(\w+)$/); + var matcher = new RegExp(lastWord[0], "i"); + if (lastWord && facetValue.search(matcher) == 0) { + boxValue = boxValue.replace(/\b(\w+)$/, ''); + } + boxValue = boxValue.replace('^\s+|\s+$', ''); + if (boxValue) { + this.app.searchBox.addFacet('text', boxValue, this.options.position); + } + return boxValue; + }, + + // Directly called to focus the input. This is different from `addFocus` + // because this is not called by a focus event. This instead calls a + // focus event causing the input to become focused. + enableEdit : function(selectText) { + this.addFocus(); + if (selectText) { + this.selectText(); + } + this.box.focus(); + }, + + // Event called on user focus on the input. Tells all other input and facets + // to give up focus, and starts revving the autocomplete. + addFocus : function() { + this.flags.canClose = false; + if (!this.app.searchBox.allSelected()) { + this.app.searchBox.disableFacets(this); + } + this.app.searchBox.addFocus(); + this.setMode('is', 'editing'); + this.setMode('not', 'selected'); + this.searchAutocomplete(); + }, + + // Directly called to blur the input. This is different from `removeFocus` + // because this is not called by a blur event. + disableEdit : function() { + this.box.blur(); + this.removeFocus(); + }, + + // Event called when user blur's the input, either through the keyboard tabbing + // away or the mouse clicking off. Cleans up + removeFocus : function() { + this.flags.canClose = false; + this.app.searchBox.removeFocus(); + this.setMode('not', 'editing'); + this.setMode('not', 'selected'); + this.closeAutocomplete(); + }, + + // When the user blurs the input, they may either be going to another input + // or off the search box entirely. If they go to another input, this facet + // will be instantly disabled, and the canClose flag will be turned back off. + // + // However, if the user clicks elsewhere on the page, this method starts a timer + // that checks if any of the other inputs are selected or are being edited. If + // not, then it can finally close itself and its autocomplete menu. + deferDisableEdit : function() { + this.flags.canClose = true; + _.delay(_.bind(function() { + if (this.flags.canClose && + !this.box.is(':focus') && + this.modes.editing == 'is') { + this.disableEdit(); + } + }, this), 250); + }, + + // Starts a timer that will cause a triple-click, which highlights all facets. + startTripleClickTimer : function() { + this.tripleClickTimer = setTimeout(_.bind(function() { + this.tripleClickTimer = null; + }, this), 500); + }, + + // Event on click that checks if a triple click is in play. The + // `tripleClickTimer` is counting down, ready to be engaged and intercept + // the click event to force a select all instead. + maybeTripleClick : function(e) { + if (!!this.tripleClickTimer) { + e.preventDefault(); + this.app.searchBox.selectAllFacets(); + return false; + } + }, + + // Is the user currently focused in the input field? + isFocused : function() { + return this.box.is(':focus'); + }, + + // When serializing the facets, the inputs need to also have their values represented, + // in case they contain text that is not yet faceted (but will be once the search is + // completed). + value : function() { + return this.box.val(); + }, + + // When switching between facets and inputs, depending on the direction the cursor + // is coming from, the cursor in this facet's input field should match the original + // direction. + setCursorAtEnd : function(direction) { + if (direction == -1) { + this.box.setCursorPosition(this.box.val().length); + } else { + this.box.setCursorPosition(0); + } + }, + + // Selects the entire range of text in the input. Useful when tabbing between inputs + // and facets. + selectText : function() { + this.box.selectRange(0, this.box.val().length); + if (!this.app.searchBox.allSelected()) { + this.box.focus(); + } else { + this.setMode('is', 'selected'); + } + }, + + // Before the searchBox performs a search, we need to close the + // autocomplete menu. + search : function(e, direction) { + if (!direction) direction = 0; + this.closeAutocomplete(); + this.app.searchBox.searchEvent(e); + _.defer(_.bind(function() { + this.app.searchBox.focusNextFacet(this, direction); + }, this)); + }, + + // Callback fired on key press in the search box. We search when they hit return. + keypress : function(e) { + var key = VS.app.hotkeys.key(e); + + if (key == 'enter') { + return this.search(e, 100); + } else if (VS.app.hotkeys.colon(e)) { + this.box.trigger('resize.autogrow', e); + var query = this.box.val(); + var prefixes = []; + if (this.app.options.callbacks.facetMatches) { + this.app.options.callbacks.facetMatches(function(p) { + prefixes = p; + }); + } + var labels = _.map(prefixes, function(prefix) { + if (prefix.label) return prefix.label; + else return prefix; + }); + if (_.contains(labels, query)) { + e.preventDefault(); + var remainder = this.addTextFacetRemainder(query); + var position = this.options.position + (remainder?1:0); + this.app.searchBox.addFacet(query, '', position); + return false; + } + } else if (key == 'backspace') { + if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + this.app.searchBox.resizeFacets(); + return false; + } + } + }, + + // Handles all keyboard inputs when in the input field. This checks + // for movement between facets and inputs, entering a new value that needs + // to be autocompleted, as well as stepping between facets with backspace. + keydown : function(e) { + var key = VS.app.hotkeys.key(e); + + if (key == 'left') { + if (this.box.getCursorPosition() == 0) { + e.preventDefault(); + this.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1}); + } + } else if (key == 'right') { + if (this.box.getCursorPosition() == this.box.val().length) { + e.preventDefault(); + this.app.searchBox.focusNextFacet(this, 1, {selectFacet: true}); + } + } else if (VS.app.hotkeys.shift && key == 'tab') { + e.preventDefault(); + this.app.searchBox.focusNextFacet(this, -1, {selectText: true}); + } else if (key == 'tab') { + e.preventDefault(); + var value = this.box.val(); + if (value.length) { + var remainder = this.addTextFacetRemainder(value); + var position = this.options.position + (remainder?1:0); + this.app.searchBox.addFacet(value, '', position); + } else { + this.app.searchBox.focusNextFacet(this, 0, { + skipToFacet: true, + selectText: true + }); + } + } else if (VS.app.hotkeys.command && + String.fromCharCode(e.which).toLowerCase() == 'a') { + e.preventDefault(); + this.app.searchBox.selectAllFacets(); + return false; + } else if (key == 'backspace' && !this.app.searchBox.allSelected()) { + if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) { + e.preventDefault(); + this.app.searchBox.focusNextFacet(this, -1, {backspace: true}); + return false; + } + } + + this.box.trigger('resize.autogrow', e); + } + +}); + +})(); + +(function(){ + + var $ = jQuery; // Handle namespaced jQuery + + // Makes the view enter a mode. Modes have both a 'mode' and a 'group', + // and are mutually exclusive with any other modes in the same group. + // Setting will update the view's modes hash, as well as set an HTML class + // of *[mode]_[group]* on the view's element. Convenient way to swap styles + // and behavior. + Backbone.View.prototype.setMode = function(mode, group) { + this.modes || (this.modes = {}); + if (this.modes[group] === mode) return; + $(this.el).setMode(mode, group); + this.modes[group] = mode; + }; + +})(); +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// DocumentCloud workspace hotkeys. To tell if a key is currently being pressed, +// just ask `VS.app.hotkeys.[key]` on `keypress`, or ask `VS.app.hotkeys.key(e)` +// on `keydown`. +// +// For the most headache-free way to use this utility, check modifier keys, +// like shift and command, with `VS.app.hotkeys.shift`, and check every other +// key with `VS.app.hotkeys.key(e) == 'key_name'`. +VS.app.hotkeys = { + + // Keys that will be mapped to the `hotkeys` namespace. + KEYS: { + '16': 'shift', + '17': 'command', + '91': 'command', + '93': 'command', + '224': 'command', + '13': 'enter', + '37': 'left', + '38': 'upArrow', + '39': 'right', + '40': 'downArrow', + '46': 'delete', + '8': 'backspace', + '9': 'tab', + '188': 'comma' + }, + + // Binds global keydown and keyup events to listen for keys that match `this.KEYS`. + initialize : function() { + _.bindAll(this, 'down', 'up', 'blur'); + $(document).bind('keydown', this.down); + $(document).bind('keyup', this.up); + $(window).bind('blur', this.blur); + }, + + // On `keydown`, turn on all keys that match. + down : function(e) { + var key = this.KEYS[e.which]; + if (key) this[key] = true; + }, + + // On `keyup`, turn off all keys that match. + up : function(e) { + var key = this.KEYS[e.which]; + if (key) this[key] = false; + }, + + // If an input is blurred, all keys need to be turned off, since they are no longer + // able to modify the document. + blur : function(e) { + for (var key in this.KEYS) this[this.KEYS[key]] = false; + }, + + // Check a key from an event and return the common english name. + key : function(e) { + return this.KEYS[e.which]; + }, + + // Colon is special, since the value is different between browsers. + colon : function(e) { + var charCode = e.which; + return charCode && String.fromCharCode(charCode) == ":"; + }, + + // Check a key from an event and match it against any known characters. + // The `keyCode` is different depending on the event type: `keydown` vs. `keypress`. + // + // These were determined by looping through every `keyCode` and `charCode` that + // resulted from `keydown` and `keypress` events and counting what was printable. + printable : function(e) { + var code = e.which; + if (e.type == 'keydown') { + if (code == 32 || // space + (code >= 48 && code <= 90) || // 0-1a-z + (code >= 96 && code <= 111) || // 0-9+-/*. + (code >= 186 && code <= 192) || // ;=,-./^ + (code >= 219 && code <= 222)) { // (\)' + return true; + } + } else { + // [space]!"#$%&'()*+,-.0-9:;<=>?@A-Z[\]^_`a-z{|} and unicode characters + if ((code >= 32 && code <= 126) || + (code >= 160 && code <= 500) || + (String.fromCharCode(code) == ":")) { + return true; + } + } + return false; + } + +}; + +})(); +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// Naive English transformations on words. Only used for a few transformations +// in VisualSearch.js. +VS.utils.inflector = { + + // Delegate to the ECMA5 String.prototype.trim function, if available. + trim : function(s) { + return s.trim ? s.trim() : s.replace(/^\s+|\s+$/g, ''); + }, + + // Escape strings that are going to be used in a regex. Escapes punctuation + // that would be incorrect in a regex. + escapeRegExp : function(s) { + return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); + } +}; + +})(); +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +$.fn.extend({ + + // Makes the selector enter a mode. Modes have both a 'mode' and a 'group', + // and are mutually exclusive with any other modes in the same group. + // Setting will update the view's modes hash, as well as set an HTML class + // of *[mode]_[group]* on the view's element. Convenient way to swap styles + // and behavior. + setMode : function(state, group) { + group = group || 'mode'; + var re = new RegExp("\\w+_" + group + "(\\s|$)", 'g'); + var mode = (state === null) ? "" : state + "_" + group; + this.each(function() { + this.className = (this.className.replace(re, '')+' '+mode) + .replace(/\s\s/g, ' '); + }); + return mode; + }, + + // When attached to an input element, this will cause the width of the input + // to match its contents. This calculates the width of the contents of the input + // by measuring a hidden shadow div that should match the styling of the input. + autoGrowInput: function() { + return this.each(function() { + var $input = $(this); + var $tester = $('<div />').css({ + opacity : 0, + top : -9999, + left : -9999, + position : 'absolute', + whiteSpace : 'nowrap' + }).addClass('VS-input-width-tester').addClass('VS-interface'); + + // Watch for input value changes on all of these events. `resize` + // event is called explicitly when the input has been changed without + // a single keypress. + var events = 'keydown.autogrow keypress.autogrow ' + + 'resize.autogrow change.autogrow'; + $input.next('.VS-input-width-tester').remove(); + $input.after($tester); + $input.unbind(events).bind(events, function(e, realEvent) { + if (realEvent) e = realEvent; + var value = $input.val(); + + // Watching for the backspace key is tricky because it may not + // actually be deleting the character, but instead the key gets + // redirected to move the cursor from facet to facet. + if (VS.app.hotkeys.key(e) == 'backspace') { + var position = $input.getCursorPosition(); + if (position > 0) value = value.slice(0, position-1) + + value.slice(position, value.length); + } else if (VS.app.hotkeys.printable(e) && + !VS.app.hotkeys.command) { + value += String.fromCharCode(e.which); + } + value = value.replace(/&/g, '&') + .replace(/\s/g,' ') + .replace(/</g, '<') + .replace(/>/g, '>'); + + $tester.html(value); + $input.width($tester.width() + 3); + $input.trigger('updated.autogrow'); + }); + + // Sets the width of the input on initialization. + $input.trigger('resize.autogrow'); + }); + }, + + + // Cross-browser method used for calculating where the cursor is in an + // input field. + getCursorPosition: function() { + var position = 0; + var input = this.get(0); + + if (document.selection) { // IE + input.focus(); + var sel = document.selection.createRange(); + var selLen = document.selection.createRange().text.length; + sel.moveStart('character', -input.value.length); + position = sel.text.length - selLen; + } else if (input && $(input).is(':visible') && + input.selectionStart != null) { // Firefox/Safari + position = input.selectionStart; + } + + return position; + }, + + // A simple proxy for `selectRange` that sets the cursor position in an + // input field. + setCursorPosition: function(position) { + return this.each(function() { + return $(this).selectRange(position, position); + }); + }, + + // Cross-browser way to select text in an input field. + selectRange: function(start, end) { + return this.each(function() { + if (this.setSelectionRange) { // FF/Webkit + this.focus(); + this.setSelectionRange(start, end); + } else if (this.createTextRange) { // IE + var range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + if (end - start >= 0) range.select(); + } + }); + }, + + // Returns an object that contains the text selection range values for + // an input field. + getSelection: function() { + var input = this[0]; + + if (input.selectionStart != null) { // FF/Webkit + var start = input.selectionStart; + var end = input.selectionEnd; + return { + start : start, + end : end, + length : end-start, + text : input.value.substr(start, end-start) + }; + } else if (document.selection) { // IE + var range = document.selection.createRange(); + if (range) { + var textRange = input.createTextRange(); + var copyRange = textRange.duplicate(); + textRange.moveToBookmark(range.getBookmark()); + copyRange.setEndPoint('EndToStart', textRange); + var start = copyRange.text.length; + var end = start + range.text.length; + return { + start : start, + end : end, + length : end-start, + text : range.text + }; + } + } + return {start: 0, end: 0, length: 0}; + } + +}); + +// Debugging in Internet Explorer. This allows you to use +// `console.log(['message', var1, var2, ...])`. Just remove the `false` and +// add your console.logs. This will automatically stringify objects using +// `JSON.stringify', so you can read what's going out. Think of this as a +// *Diet Firebug Lite Zero with Lemon*. +if ($.browser.msie && false) { + window.console = {}; + var _$ied; + window.console.log = function(msg) { + if (_.isArray(msg)) { + var message = msg[0]; + var vars = _.map(msg.slice(1), function(arg) { + return JSON.stringify(arg); + }).join(' - '); + } + if(!_$ied){ + _$ied = $('<div><ol></ol></div>').css({ + 'position': 'fixed', + 'bottom': 10, + 'left': 10, + 'zIndex': 20000, + 'width': $('body').width() - 80, + 'border': '1px solid #000', + 'padding': '10px', + 'backgroundColor': '#fff', + 'fontFamily': 'arial,helvetica,sans-serif', + 'fontSize': '11px' + }); + $('body').append(_$ied); + } + var $message = $('<li>'+message+' - '+vars+'</li>').css({ + 'borderBottom': '1px solid #999999' + }); + _$ied.find('ol').append($message); + _.delay(function() { + $message.fadeOut(500); + }, 5000); + }; + +} + +})(); + +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// Used to extract keywords and facets from the free text search. +var FREETEXT_RE = '(\'[^\']+\'|"[^"]+"|[^\'"\\s]\\S*)'; +var CATEGORY_RE = FREETEXT_RE + ':\\s*'; +VS.app.SearchParser = { + + // Matches `category: "free text"`, with and without quotes. + ALL_FIELDS : new RegExp(CATEGORY_RE + FREETEXT_RE, 'g'), + + // Matches a single category without the text. Used to correctly extract facets. + CATEGORY : new RegExp(CATEGORY_RE), + + // Called to parse a query into a collection of `SearchFacet` models. + parse : function(instance, query) { + var searchFacets = this._extractAllFacets(instance, query); + instance.searchQuery.reset(searchFacets); + return searchFacets; + }, + + // Walks the query and extracts facets, categories, and free text. + _extractAllFacets : function(instance, query) { + var facets = []; + var originalQuery = query; + + while (query) { + var category, value; + originalQuery = query; + var field = this._extractNextField(query); + if (!field) { + category = 'text'; + value = this._extractSearchText(query); + query = VS.utils.inflector.trim(query.replace(value, '')); + } else if (field.indexOf(':') != -1) { + category = field.match(this.CATEGORY)[1].replace(/(^['"]|['"]$)/g, ''); + value = field.replace(this.CATEGORY, '').replace(/(^['"]|['"]$)/g, ''); + query = VS.utils.inflector.trim(query.replace(field, '')); + } else if (field.indexOf(':') == -1) { + category = 'text'; + value = field; + query = VS.utils.inflector.trim(query.replace(value, '')); + } + + if (category && value) { + var searchFacet = new VS.model.SearchFacet({ + category : category, + value : VS.utils.inflector.trim(value), + app : instance + }); + facets.push(searchFacet); + } + if (originalQuery == query) break; + } + + return facets; + }, + + // Extracts the first field found, capturing any free text that comes + // before the category. + _extractNextField : function(query) { + var textRe = new RegExp('^\\s*(\\S+)\\s+(?=' + CATEGORY_RE + FREETEXT_RE + ')'); + var textMatch = query.match(textRe); + if (textMatch && textMatch.length >= 1) { + return textMatch[1]; + } else { + return this._extractFirstField(query); + } + }, + + // If there is no free text before the facet, extract the category and value. + _extractFirstField : function(query) { + var fields = query.match(this.ALL_FIELDS); + return fields && fields.length && fields[0]; + }, + + // If the found match is not a category and facet, extract the trimmed free text. + _extractSearchText : function(query) { + query = query || ''; + var text = VS.utils.inflector.trim(query.replace(this.ALL_FIELDS, '')); + return text; + } + +}; + +})(); + +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// The model that holds individual search facets and their categories. +// Held in a collection by `VS.app.searchQuery`. +VS.model.SearchFacet = Backbone.Model.extend({ + + // Extract the category and value and serialize it in preparation for + // turning the entire searchBox into a search query that can be sent + // to the server for parsing and searching. + serialize : function() { + var category = this.quoteCategory(this.get('category')); + var value = VS.utils.inflector.trim(this.get('value')); + + if (!value) return ''; + + if (!_.contains(this.get("app").options.unquotable || [], category) && category != 'text') { + value = this.quoteValue(value); + } + + if (category != 'text') { + category = category + ': '; + } else { + category = ""; + } + return category + value; + }, + + // Wrap categories that have spaces or any kind of quote with opposite matching + // quotes to preserve the complex category during serialization. + quoteCategory : function(category) { + var hasDoubleQuote = (/"/).test(category); + var hasSingleQuote = (/'/).test(category); + var hasSpace = (/\s/).test(category); + + if (hasDoubleQuote && !hasSingleQuote) { + return "'" + category + "'"; + } else if (hasSpace || (hasSingleQuote && !hasDoubleQuote)) { + return '"' + category + '"'; + } else { + return category; + } + }, + + // Wrap values that have quotes in opposite matching quotes. If a value has + // both single and double quotes, just use the double quotes. + quoteValue : function(value) { + var hasDoubleQuote = (/"/).test(value); + var hasSingleQuote = (/'/).test(value); + + if (hasDoubleQuote && !hasSingleQuote) { + return "'" + value + "'"; + } else { + return '"' + value + '"'; + } + } + +}); + +})(); +(function() { + +var $ = jQuery; // Handle namespaced jQuery + +// Collection which holds all of the individual facets (category: value). +// Used for finding and removing specific facets. +VS.model.SearchQuery = Backbone.Collection.extend({ + + // Model holds the category and value of the facet. + model : VS.model.SearchFacet, + + // Turns all of the facets into a single serialized string. + serialize : function() { + return this.map(function(facet){ return facet.serialize(); }).join(' '); + }, + + facets : function() { + return this.map(function(facet) { + var value = {}; + value[facet.get('category')] = facet.get('value'); + return value; + }); + }, + + // Find a facet by its category. Multiple facets with the same category + // is fine, but only the first is returned. + find : function(category) { + var facet = this.detect(function(facet) { + return facet.get('category') == category; + }); + return facet && facet.get('value'); + }, + + // Counts the number of times a specific category is in the search query. + count : function(category) { + return this.select(function(facet) { + return facet.get('category') == category; + }).length; + }, + + // Returns an array of extracted values from each facet in a category. + values : function(category) { + var facets = this.select(function(facet) { + return facet.get('category') == category; + }); + return _.map(facets, function(facet) { return facet.get('value'); }); + }, + + // Checks all facets for matches of either a category or both category and value. + has : function(category, value) { + return this.any(function(facet) { + var categoryMatched = facet.get('category') == category; + if (!value) return categoryMatched; + return categoryMatched && facet.get('value') == value; + }); + }, + + // Used to temporarily hide a specific category and serialize the search query. + withoutCategory : function(category) { + return this.map(function(facet) { + if (facet.get('category') != category) return facet.serialize(); + }).join(' '); + } + +}); + +})();(function(){ +window.JST = window.JST || {}; + +window.JST['search_box'] = _.template('<div class="VS-search">\n <div class="VS-search-box-wrapper VS-search-box">\n <div class="VS-icon VS-icon-search"></div>\n <div class="VS-search-inner"></div>\n <div class="VS-icon VS-icon-cancel VS-cancel-search-box" title="clear search"></div>\n </div>\n</div>'); +window.JST['search_facet'] = _.template('<% if (model.has(\'category\')) { %>\n <div class="category"><%= model.get(\'category\') %>:</div>\n<% } %>\n\n<div class="search_facet_input_container">\n <input type="text" class="search_facet_input VS-interface" value="" />\n</div>\n\n<div class="search_facet_remove VS-icon VS-icon-cancel"></div>'); +window.JST['search_input'] = _.template('<input type="text" />'); +})();
\ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/demo.html b/addons/web/static/lib/visualsearch/demo.html new file mode 100755 index 00000000000..212fb7cee28 --- /dev/null +++ b/addons/web/static/lib/visualsearch/demo.html @@ -0,0 +1,453 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> + <meta http-equiv="X-UA-Compatible" content="chrome=1"> + <title>DocumentCloud's VisualSearch.js</title> + <style> + body { + font-size: 16px; + line-height: 24px; + background: #FEF3CA; + color: #022; + height: 100%; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + } + div.container { + width: 720px; + margin: 50px 0 50px 50px; + } + p, li { + margin: 16px 0 16px 0; + width: 550px; + } + p.break { + margin-top: 35px; + } + ol { + padding-left: 24px; + } + ol li { + font-weight: bold; + margin-left: 0; + } + a, a:visited { + padding: 0 2px; + text-decoration: none; + background: #f0c095; + color: #252519; + } + a:active, a:hover { + color: #FFF; + background: #C25D00; + } + h1, h2, h3, h4, h5, h6 { + margin-top: 40px; + } + b.header { + font-size: 18px; + } + span.alias { + font-size: 14px; + font-style: italic; + margin-left: 20px; + } + table { + margin: 16px 0; padding: 0; + } + tr, td, th { + margin: 0; padding: 0; + text-align: left; + } + th { + padding: 24px 0 0; + } + tr:first-child th { + padding-top: 0; + } + td { + padding: 6px 15px 6px 0; + } + td.definition { + line-height: 18px; + font-size: 14px; + } + table.downloads td { + padding-left: 18px; + } + .demo-hint { + font-size: 13px; + margin: 0 0 12px 12px; + font-weight: normal; + } + #VS code, #VS pre, #VS tt { + font-family: Monaco, Consolas, "Lucida Console", monospace; + font-size: 12px; + line-height: 18px; + color: #444; + background: none; + } + #VS code { + margin-left: 8px; + padding: 0 0 0 12px; + font-weight: normal; + } + #VS pre { + font-size: 12px; + padding: 2px 0 2px 0; + border-left: 6px solid #829C37; + margin: 12px 0; + } + #search_query { + margin: 18px 0; + opacity: 0; + } + #search_query .raquo { + font-size: 18px; + line-height: 12px; + font-weight: bold; + margin-right: 4px; + } + #search_query2 { + margin: 18px 0; + opacity: 0; + } + #search_query2 .raquo { + font-size: 18px; + line-height: 12px; + font-weight: bold; + margin-right: 4px; + } + </style> + + <link rel="stylesheet" href="lib/css/reset.css" type="text/css" media="screen" charset="utf-8"> + <link rel="stylesheet" href="lib/css/icons.css" type="text/css" media="screen" charset="utf-8"> + <link rel="stylesheet" href="lib/css/workspace.css" type="text/css" media="screen" charset="utf-8"> + + <script src="vendor/jquery-1.6.1.js" type="text/javascript" charset="utf-8"></script> + <!-- <script src="vendor/backported/jquery-1.4.4.js" type="text/javascript" charset="utf-8"></script> --> + <script src="vendor/jquery.ui.core.js" type="text/javascript" charset="utf-8"></script> + <script src="vendor/jquery.ui.widget.js" type="text/javascript" charset="utf-8"></script> + <script src="vendor/jquery.ui.position.js" type="text/javascript" charset="utf-8"></script> + <script src="vendor/jquery.ui.autocomplete.js" type="text/javascript" charset="utf-8"></script> + <script src="vendor/underscore-1.1.5.js" type="text/javascript" charset="utf-8"></script> + <script src="vendor/backbone-0.5.0.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/visualsearch.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/views/search_box.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/views/search_facet.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/views/search_input.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/models/search_facets.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/models/search_query.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/utils/backbone_extensions.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/utils/hotkeys.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/utils/jquery_extensions.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/utils/search_parser.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/utils/inflector.js" type="text/javascript" charset="utf-8"></script> + <script src="lib/js/templates/templates.js" type="text/javascript" charset="utf-8"></script> +</head> +<body> + + <div class="container" id="VS"> + + <h1><a href="index.html">VisualSearch.js</a></h1> + + <p> + <a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a> + enhances ordinary search boxes with the ability to autocomplete + faceted search queries. Specify the facets for completion, along with the + completable values for any facet. You can retrieve the search query as a + structured object, so you don't have to parse the query string yourself. + </p> + + <p> + <a href="docs/visualsearch.html">The complete annotated source code</a> + is also available. + </p> + + <p> + The project is + <a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>. + You can report bugs and discuss features on the + <a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>, + on Freenode in the <tt>#documentcloud</tt> channel, + or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>. + </p> + + <p> + <i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>.</i> + </p> + <h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2> + + <div id="search_box_container"></div> + <div id="search_query"> </div> + + <script type="text/javascript" charset="utf-8"> + $(document).ready(function() { + window.visualSearch = VS.init({ + container : $('#search_box_container'), + query : 'country: "South Africa" account: 5-samuel title: "Pentagon Papers"', + // query : '', + unquotable : [ + 'text', + 'account', + 'filter', + 'access' + ], + callbacks : { + search : function(query, searchCollection) { + var $query = $('#search_query'); + $query.stop().animate({opacity : 1}, {duration: 300, queue: false}); + $query.html('<span class="raquo">»</span> You searched for: <b>' + searchCollection.serialize() + '</b>'); + clearTimeout(window.queryHideDelay); + window.queryHideDelay = setTimeout(function() { + $query.animate({ + opacity : 0 + }, { + duration: 1000, + queue: false + }); + }, 2000); + }, + valueMatches : function(category, searchTerm, callback) { + switch (category) { + case 'account': + callback([ + { value: '1-amanda', label: 'Amanda' }, + { value: '2-aron', label: 'Aron' }, + { value: '3-eric', label: 'Eric' }, + { value: '4-jeremy', label: 'Jeremy' }, + { value: '5-samuel', label: 'Samuel' }, + { value: '6-scott', label: 'Scott' } + ]); + break; + case 'filter': + callback(['published', 'unpublished', 'draft']); + break; + case 'access': + callback(['public', 'private', 'protected']); + break; + case 'title': + callback([ + 'Pentagon Papers', + 'CoffeeScript Manual', + 'Laboratory for Object Oriented Thinking', + 'A Repository Grows in Brooklyn' + ]); + break; + case 'city': + callback([ + 'Cleveland', + 'New York City', + 'Brooklyn', + 'Manhattan', + 'Queens', + 'The Bronx', + 'Staten Island', + 'San Francisco', + 'Los Angeles', + 'Seattle', + 'London', + 'Portland', + 'Chicago', + 'Boston' + ]) + break; + case 'state': + callback([ + "Alabama", "Alaska", "Arizona", "Arkansas", "California", + "Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida", + "Georgia", "Guam", "Hawaii", "Idaho", "Illinois", + "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", + "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", + "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", + "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", + "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", + "Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", + "Texas", "Utah", "Vermont", "Virginia", "Virgin Islands", + "Washington", "West Virginia", "Wisconsin", "Wyoming" + ]); + break + case 'country': + callback([ + "China", "India", "United States", "Indonesia", "Brazil", + "Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan", + "Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt", + "Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo", + "France", "United Kingdom", "Italy", "Myanmar", "South Africa", + "South Korea", "Colombia", "Ukraine", "Spain", "Tanzania", + "Sudan", "Kenya", "Argentina", "Poland", "Algeria", + "Canada", "Uganda", "Morocco", "Iraq", "Nepal", + "Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan", + "Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique", + "Taiwan", "Syria", "Ivory Coast", "Australia", "Romania", + "Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile", + "Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi", + "Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia", + "Senegal", "Zimbabwe", "Chad", "Cuba", "Greece", + "Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea", + "Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary", + "Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan", + "Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria", + "Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea", + "Togo", "Libya", "Jordan", "Paraguay", "Laos", + "El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark", + "Slovakia", "Finland", "Eritrea", "Turkmenistan" + ], {preserveOrder: true}); + break; + } + }, + facetMatches : function(callback) { + callback([ + 'account', 'filter', 'access', 'title', + { label: 'city', category: 'location' }, + { label: 'address', category: 'location' }, + { label: 'country', category: 'location' }, + { label: 'state', category: 'location' }, + ]); + } + } + }); + }); + </script> + + + + <div id="search_box_container2"></div> + <div id="search_query2"> </div> + + <script type="text/javascript" charset="utf-8"> + $(document).ready(function() { + var visualSearch = VS.init({ + container : $('#search_box_container2'), + query : '', + // query : '', + unquotable : [ + 'text', + 'account', + 'filter', + 'access' + ], + callbacks : { + search : function(query, searchCollection) { + console.log(["query", searchCollection.facets(), query]); + var $query = $('#search_query2'); + $query.stop().animate({opacity : 1}, {duration: 300, queue: false}); + $query.html('<span class="raquo">»</span> You searched for: <b>' + searchCollection.serialize() + '</b>'); + clearTimeout(window.queryHideDelay2); + window.queryHideDelay2 = setTimeout(function() { + $query.animate({ + opacity : 0 + }, { + duration: 1000, + queue: false + }); + }, 2000); + }, + valueMatches : function(category, searchTerm, callback) { + switch (category) { + case 'account': + callback([ + { value: '1-amanda', label: 'Amanda' }, + { value: '2-aron', label: 'Aron' }, + { value: '3-eric', label: 'Eric' }, + { value: '4-jeremy', label: 'Jeremy' }, + { value: '5-samuel', label: 'Samuel' }, + { value: '6-scott', label: 'Scott' } + ]); + break; + case 'filter': + callback(['published', 'unpublished', 'draft']); + break; + case 'access': + callback(['public', 'private', 'protected']); + break; + case 'title': + callback([ + 'Pentagon Papers', + 'CoffeeScript Manual', + 'Laboratory for Object Oriented Thinking', + 'A Repository Grows in Brooklyn' + ]); + break; + case 'city': + callback([ + 'Cleveland', + 'New York City', + 'Brooklyn', + 'Manhattan', + 'Queens', + 'The Bronx', + 'Staten Island', + 'San Francisco', + 'Los Angeles', + 'Seattle', + 'London', + 'Portland', + 'Chicago', + 'Boston' + ]) + break; + case 'state': + callback([ + "Alabama", "Alaska", "Arizona", "Arkansas", "California", + "Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida", + "Georgia", "Guam", "Hawaii", "Idaho", "Illinois", + "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", + "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", + "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", + "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", + "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", + "Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", + "Texas", "Utah", "Vermont", "Virginia", "Virgin Islands", + "Washington", "West Virginia", "Wisconsin", "Wyoming" + ]); + break + case 'country': + callback([ + "China", "India", "United States", "Indonesia", "Brazil", + "Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan", + "Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt", + "Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo", + "France", "United Kingdom", "Italy", "Myanmar", "South Africa", + "South Korea", "Colombia", "Ukraine", "Spain", "Tanzania", + "Sudan", "Kenya", "Argentina", "Poland", "Algeria", + "Canada", "Uganda", "Morocco", "Iraq", "Nepal", + "Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan", + "Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique", + "Taiwan", "Syria", "Ivory Coast", "Australia", "Romania", + "Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile", + "Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi", + "Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia", + "Senegal", "Zimbabwe", "Chad", "Cuba", "Greece", + "Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea", + "Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary", + "Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan", + "Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria", + "Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea", + "Togo", "Libya", "Jordan", "Paraguay", "Laos", + "El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark", + "Slovakia", "Finland", "Eritrea", "Turkmenistan" + ]); + break; + } + }, + facetMatches : function(callback) { + callback([ + 'account', 'filter', 'access', 'title', + { label: 'city', category: 'location' }, + { label: 'address', category: 'location' }, + { label: 'country', category: 'location' }, + { label: 'state', category: 'location' }, + ], { + preserveOrder: true + }); + } + } + }); + }); + </script> + + </div> + +</body> +</html> diff --git a/addons/web/static/lib/visualsearch/index.html b/addons/web/static/lib/visualsearch/index.html new file mode 100755 index 00000000000..07cc8de20e8 --- /dev/null +++ b/addons/web/static/lib/visualsearch/index.html @@ -0,0 +1,537 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> + <meta http-equiv="X-UA-Compatible" content="chrome=1"> + <title>DocumentCloud's VisualSearch.js</title> + <style> + body { + font-size: 16px; + line-height: 24px; + background: #FEF3CA; + height: 100%; + color: #022; + font-family: Arial; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + } + div.container { + width: 720px; + margin: 50px 0 50px 50px; + } + p, li { + margin: 16px 0 16px 0; + width: 550px; + } + p.break { + margin-top: 35px; + } + ol { + padding-left: 24px; + } + ol li { + font-weight: bold; + margin-left: 0; + } + a, a:visited { + padding: 0 2px; + text-decoration: none; + background: #f0c095; + color: #252519; + } + a:active, a:hover { + color: #000; + background: #e0a070; + } + h1, h2, h3, h4, h5, h6 { + margin-top: 40px; + } + b.header { + font-size: 18px; + } + span.alias { + font-size: 14px; + font-style: italic; + margin-left: 20px; + } + table { + margin: 16px 0; padding: 0; + } + tr, td, th { + margin: 0; padding: 0; + text-align: left; + } + th { + padding: 24px 0 0; + } + tr:first-child th { + padding-top: 0; + } + td { + padding: 6px 15px 6px 0; + } + td.definition { + line-height: 18px; + font-size: 14px; + } + table.downloads td { + padding-left: 18px; + } + .demo-hint { + font-size: 13px; + margin: 0 0 12px 12px; + font-weight: normal; + } + #VS code, #VS pre, #VS tt { + font-family: Monaco, Consolas, "Lucida Console", monospace; + font-size: 12px; + line-height: 18px; + color: #444; + background: none; + } + #VS code { + margin-left: 8px; + padding: 0 0 0 12px; + font-weight: normal; + } + #VS pre { + font-size: 12px; + padding: 2px 0 2px 0; + border-left: 6px solid #829C37; + margin: 12px 0; + } + #search_query { + margin: 18px 0 -24px; + opacity: 0; + } + #search_query .raquo { + font-size: 18px; + line-height: 12px; + font-weight: bold; + margin-right: 4px; + } + .attribution { + margin: -12px 0 24px; + font-size: 14px; + } + </style> + + <link rel="stylesheet" href="build-min/visualsearch-datauri.css" type="text/css" media="screen" charset="utf-8"> + <link rel="stylesheet" href="docs/assets/github.css" type="text/css" media="screen" charset="utf-8"> + + <script src="build-min/dependencies.js" type="text/javascript" charset="utf-8"></script> + <script src="build-min/visualsearch.js" type="text/javascript" charset="utf-8"></script> + <script src="docs/assets/highlight.js" type="text/javascript" charset="utf-8"></script> + <script src="docs/assets/javascript.js" type="text/javascript" charset="utf-8"></script> + <script src="docs/assets/xml.js" type="text/javascript" charset="utf-8"></script> + <script> + hljs.initHighlightingOnLoad(); + </script> + +</head> +<body> + + <div class="container" id="VS"> + + <h1>VisualSearch.js</h1> + + <p class="attribution"> + <i> + Created by <a href="http://github.com/samuelclay">Samuel Clay</a>, + <a href="http://twitter.com/samuelclay">@samuelclay</a>. + </i> + </p> + + <p> + <a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a> + enhances ordinary search boxes with the ability to autocomplete + faceted search queries. Specify the facets for completion, along with the + completable values for any facet. You can retrieve the search query as a + structured object, so you don't have to parse the query string yourself. + </p> + + <p> + <a href="https://www.documentcloud.org/public/#search/group%3A%20dcloud%20chair%3A%20%22Christopher%20Dodd%22%20TARP">Here's an example of a search on DocumentCloud.org that uses facets.</a> + </p> + + <p> + The project is + <a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>. + You can report bugs and discuss features on the + <a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>, + on Freenode in the <tt>#documentcloud</tt> channel, + or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>. + </p> + + <p> + <i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>. + <br /> + <a href="docs/visualsearch.html">The complete annotated source code</a> + is also available.</i> + </p> + + <h2>Table of Contents</h2> + + <a href="#demo">Demo</a> | <a href="#downloads">Downloads</a> | <a href="#usage">Usage</a> | <a href="#changelog">Change Log</a> + + + <h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2> + + + + <div id="search_box_container"></div> + <div id="search_query"> </div> + + <script type="text/javascript" charset="utf-8"> + $(document).ready(function() { + var visualSearch = VS.init({ + container : $('#search_box_container'), + query : 'country: "United States" state: "New York" account: 5-samuel title: "Pentagon Papers"', + unquotable : [ + 'text', + 'account', + 'filter', + 'access' + ], + callbacks : { + search : function(query, searchCollection) { + var $query = $('#search_query'); + var count = searchCollection.size(); + $query.stop().animate({opacity : 1}, {duration: 300, queue: false}); + $query.html('<span class="raquo">»</span> You searched for: ' + + '<b>' + (query || '<i>nothing</i>') + '</b>. ' + + '(' + count + ' facet' + (count==1 ? '' : 's') + ')'); + clearTimeout(window.queryHideDelay); + window.queryHideDelay = setTimeout(function() { + $query.animate({ + opacity : 0 + }, { + duration: 1000, + queue: false + }); + }, 2000); + }, + facetMatches : function(callback) { + callback([ + 'account', 'filter', 'access', 'title', + { label: 'city', category: 'location' }, + { label: 'country', category: 'location' }, + { label: 'state', category: 'location' }, + ]); + }, + valueMatches : function(facet, searchTerm, callback) { + switch (facet) { + case 'account': + callback([ + { value: '1-amanda', label: 'Amanda' }, + { value: '2-aron', label: 'Aron' }, + { value: '3-eric', label: 'Eric' }, + { value: '4-jeremy', label: 'Jeremy' }, + { value: '5-samuel', label: 'Samuel' }, + { value: '6-scott', label: 'Scott' } + ]); + break; + case 'filter': + callback(['published', 'unpublished', 'draft']); + break; + case 'access': + callback(['public', 'private', 'protected']); + break; + case 'title': + callback([ + 'Pentagon Papers', + 'CoffeeScript Manual', + 'Laboratory for Object Oriented Thinking', + 'A Repository Grows in Brooklyn' + ]); + break; + case 'city': + callback([ + 'Cleveland', + 'New York City', + 'Brooklyn', + 'Manhattan', + 'Queens', + 'The Bronx', + 'Staten Island', + 'San Francisco', + 'Los Angeles', + 'Seattle', + 'London', + 'Portland', + 'Chicago', + 'Boston' + ]); + break; + case 'state': + callback([ + "Alabama", "Alaska", "Arizona", "Arkansas", "California", + "Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida", + "Georgia", "Guam", "Hawaii", "Idaho", "Illinois", + "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", + "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", + "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", + "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", + "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", + "Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", + "Texas", "Utah", "Vermont", "Virginia", "Virgin Islands", + "Washington", "West Virginia", "Wisconsin", "Wyoming" + ]); + break; + case 'country': + callback([ + "China", "India", "United States", "Indonesia", "Brazil", + "Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan", + "Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt", + "Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo", + "France", "United Kingdom", "Italy", "Myanmar", "South Africa", + "South Korea", "Colombia", "Ukraine", "Spain", "Tanzania", + "Sudan", "Kenya", "Argentina", "Poland", "Algeria", + "Canada", "Uganda", "Morocco", "Iraq", "Nepal", + "Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan", + "Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique", + "Taiwan", "Syria", "Ivory Coast", "Australia", "Romania", + "Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile", + "Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi", + "Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia", + "Senegal", "Zimbabwe", "Chad", "Cuba", "Greece", + "Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea", + "Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary", + "Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan", + "Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria", + "Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea", + "Togo", "Libya", "Jordan", "Paraguay", "Laos", + "El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark", + "Slovakia", "Finland", "Eritrea", "Turkmenistan" + ], {preserveOrder: true}); + break; + } + } + } + }); + }); + </script> + + <h2 id="downloads">Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and use "Save As")</i></h2> + + <table class="downloads"> + + <tr> + <th colspan="2">0. Everything (<tt>visualsearch.zip</tt>)</th> + </tr> + <tr> + <td><a href="https://github.com/documentcloud/visualsearch/zipball/master">Download everything</a></td> + </tr> + + <tr> + <th colspan="2">1. VisualSearch JavaScript (<tt>visualsearch.js</tt>)</th> + </tr> + <tr> + <td><a href="build-min/visualsearch.js">Production Version (0.2.2)</a></td> + <td><i>8kb, Minified and Gzipped</i></td> + </tr> + <tr> + <td><a href="build/visualsearch.js">Development Version (0.2.2)</a></td> + <td><i>45kb, Uncompressed with Comments</i></td> + </tr> + + <tr> + <th colspan="2">2. VisualSearch Stylesheets (<tt>visualsearch.css</tt>)</th> + </tr> + <tr> + <td colspan="2"><i>You should include both the datauri and image urls versions. <a href="#css">See how to include both</a></i></td> + </tr> + <tr> + <td><a href="build-min/visualsearch-datauri.css">Production Version - datauri</a></td> + <td><i>4kb, Minified and Gzipped</i></td> + </tr> + <tr> + <td><a href="build-min/visualsearch.css">Production Version - image urls</a></td> + <td><i>4kb, Minified and Gzipped</i></td> + </tr> + <tr> + <td><a href="build/visualsearch.css">Development Version</a></td> + <td><i>8kb, Uncompressed with Comments</i></td> + </tr> + <tr> + <th colspan="2">3. VisualSearch Images</th> + </tr> + <tr> + <td><img src="lib/images/embed/icons/search_glyph.png"> <a href="lib/images/embed/icons/search_glyph.png">Search Glyph</a></td> + <td><i>4kb, embedded in visualsearch-datauri.css</i></td> + </tr> + <tr> + <td><img src="lib/images/embed/icons/cancel_search.png"> <a href="lib/images/embed/icons/cancel_search.png">Cancel Button</a></td> + <td><i>4kb, embedded in visualsearch-datauri.css</i></td> + </tr> + + <tr> + <th colspan="2">4. VisualSearch Dependencies (<tt>jQuery 1.4+, jQuery UI, Underscore.js, Backbone.js</tt>)</th> + </tr> + <tr> + <td colspan="2"><i>You should only include the dependencies you don't already have.</i></td> + </tr> + <tr> + <td><a href="build-min/dependencies.js">Production Version - All</a></td> + <td><i>49kb, Minified and Gzipped</i></td> + </tr> + <tr> + <td><a href="build/dependencies.js">Development Version - All</a></td> + <td><i>340kb, Uncompressed with Comments</i></td> + </tr> + <tr> + <td><a href="vendor/jquery-1.6.1.js">jQuery 1.6.1</a></td> + <td><i>238kb, Uncompressed with Comments</i></td> + </tr> + <tr> + <td>jQuery UI 1.8.13: <br /><a href="vendor/jquery.ui.core.js">Core</a> <a href="vendor/jquery.ui.position.js">Position</a> <a href="vendor/jquery.ui.widget.js">Widget</a> <a href="vendor/jquery.ui.autocomplete.js">Autocomplete</a></td> + <td><i>48kb, Uncompressed with Comments</i></td> + </tr> + <tr> + <td><a href="vendor/underscore-1.1.5.js">Underscore 1.1.5</a></td> + <td><i>29kb, Uncompressed with Comments</i></td> + </tr> + <tr> + <td><a href="vendor/backbone-0.5.0.js">Backbone 0.5.0</a></td> + <td><i>41kb, Uncompressed with Comments</i></td> + </tr> + + </table> + + <h2 id="usage">Usage</h2> + + <p>To use VisualSearch.js on your site, follow these instructions on installation, configuration, and customization.</p> + + <ol> + <li class="xml" id="css">Insert the JavaScript and CSS into your page:<br /> + <pre><code><script src="visualsearch.js" type="text/javascript"></script> +<!--[if (!IE)|(gte IE 8)]><!--> + <link href="visualsearch-datauri.css" media="screen" rel="stylesheet" type="text/css" /> +<!--<![endif]--> +<!--[if lte IE 7]><!--> + <link href="visualsearch.css" media="screen" rel="stylesheet" type="text/css" /> +<!--<![endif]--></code></pre> + </li> + <li>Initialize the Visual Search box:<br /> + <pre class="javascript"><code><div class="visual_search"></div> + +<script type="text/javascript" charset="utf-8"> + $(document).ready(function() { + var visualSearch = VS.init({ + container : $('.visual_search'), + query : '', + callbacks : { + search : function(query, searchCollection) {}, + facetMatches : function(callback) {}, + valueMatches : function(facet, searchTerm, callback) {} + } + }); + }); +</script></code></pre> + </li> + <li class="javascript">Customize the autocompleted facets and values: + <pre><code>callbacks : { + ... + // These are the facets that will be autocompleted in an empty input. + facetMatches : function(callback) { + callback([ + 'account', 'filter', 'access', 'title', + { label: 'city', category: 'location' }, + { label: 'address', category: 'location' }, + { label: 'country', category: 'location' }, + { label: 'state', category: 'location' }, + ]); + } + ... + // These are the values that match specific categories, autocompleted + // in a category's input field. searchTerm can be used to filter the + // list on the server-side, prior to providing a list to the widget. + valueMatches : function(facet, searchTerm, callback) { + switch (facet) { + case 'account': + callback([ + { value: '1-amanda', label: 'Amanda' }, + { value: '2-aron', label: 'Aron' }, + { value: '3-eric', label: 'Eric' }, + { value: '4-jeremy', label: 'Jeremy' }, + { value: '5-samuel', label: 'Samuel' }, + { value: '6-scott', label: 'Scott' } + ]); + break; + case 'filter': + callback(['published', 'unpublished', 'draft']); + break; + case 'access': + callback(['public', 'private', 'protected']); + break; + case 'title': + callback([ + 'Pentagon Papers', + 'CoffeeScript Manual', + 'Laboratory for Object Oriented Thinking', + 'A Repository Grows in Brooklyn' + ]); + break; + } + } +... +}</code></pre> + </li> + <li class="javascript">Inspect the Visual Search box + <pre><code>// Returns the unstructured search query +visualSearch.searchBox.value() +// "country: "South Africa" account: 5-samuel title: "Pentagon Papers"" + +// Returns an array of Facet model instances +visualSearch.searchQuery.facets() +// [FacetModel<country:"South Africa">, +// FacetModel<account:5-samuel>, +// FacetModel<title:"Pentagon Papers">] + +// Set the search query with raw text +visualSearch.searchBox.value("Country: US State: \"New York\" Key: Value") + +</code></pre> + </li> + </ol> + + + <h2 id="changelog">Change Log</h2> + + <p> + <b class="header">0.2.2</b> <i>March 10th, 2012</i><br /> + If you do not want to automatically filter the value matches, you can pass an + options hash with <tt>preserveMatches: true</tt> as the second argument to the callback. + <a href="https://github.com/documentcloud/visualsearch/pull/44">See pull request #44</a> for details. + </p> + <p> + <b class="header">0.2.1</b> <i>November 14th, 2011</i><br /> + The autocompleted facets and values that are provided by your callbacks <tt>facetMatches</tt> + and <tt>valueMatches</tt> can now preserve the order of items you give them. Simply pass an + options hash with <tt>preserveOrder: true</tt> as the second argument to the callback. See + <a href="demo.html">the demo page</a> for an example. + </p> + <p> + <b class="header">0.2.0</b> <i>August 10th, 2011</i><br /> + Multiple instances of VisualSearch on a single page. <tt>VS.init</tt> now returns + a reference to the instance. The <tt>search</tt> callback now contains both the + serialized search query and a reference to the search query collection (as a + <a href="http://documentcloud.github.com/backbone/#Collection">Backbone.Collection</a>), + which can be used to manipulate each facet directly. See + <a href="docs/search_query.html">the source code for search_query.js</a> for available + methods on the collection. + </p> + <p> + <b class="header">0.1.0</b> <i>June 23rd, 2011</i><br /> + Initial release of VisualSearch.js. + </p> + + <p> + <a href="http://documentcloud.org/" title="A DocumentCloud Project" style="background:none;"> + <img src="http://jashkenas.s3.amazonaws.com/images/a_documentcloud_project.png" alt="A DocumentCloud Project" /> + </a> + </p> + + </div> + +</body> +</html> diff --git a/addons/web/static/src/css/Makefile b/addons/web/static/src/css/Makefile new file mode 100644 index 00000000000..eca044497ef --- /dev/null +++ b/addons/web/static/src/css/Makefile @@ -0,0 +1,3 @@ +base.css: base.sass + sass -t expanded base.sass base.css + diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index b0a2b7945b9..854946cdf97 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,37 +1,428 @@ -.openerp2 { +@charset "utf-8"; +@font-face { + font-family: "mnmliconsRegular"; + src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); + src: url("/web/static/src/font/mnmliconsv21-webfont.woff") format("woff"); + src: url("/web/static/src/font/mnmliconsv21-webfont.ttf") format("truetype"); + src: url("/web/static/src/font/mnmliconsv21-webfont.svg") format("svg") active; + font-weight: normal; + font-style: normal; +} + +.openerp.openerp-web-client-container { + height: 100%; + position: relative; +} + +.openerp { padding: 0; margin: 0; font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif; color: #4c4c4c; font-size: 13px; background: white; - position: relative; + /* http://www.quirksmode.org/dom/inputfile.html + * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image + */ } -.openerp2 a { +.openerp a { text-decoration: none; } -.openerp2 .oe_webclient { - position: absolute; +.openerp table { + padding: 0; + font-size: 13px; + border-collapse: collapse; +} +.openerp thead { + font-weight: bold; + background-color: #f0f0f0; +} +.openerp thead th { + border-right: 1px dotted #afafb6; +} +.openerp thead th:last-child { + border-right: none; +} +.openerp th, .openerp td { + padding: 0; + line-height: 18px; + text-align: left; +} +.openerp th { + font-weight: bold; + vertical-align: middle; +} +.openerp td { + vertical-align: top; +} +.openerp .zebra tbody tr:nth-child(odd) td { + background-color: #f0f0fa; + background-color: #f0f0fa; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6)); + background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6); +} +.openerp .zebra tbody tr:hover td { + background-color: #eeeeee; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#dedede)); + background-image: -webkit-linear-gradient(top, #eeeeee, #dedede); + background-image: -moz-linear-gradient(top, #eeeeee, #dedede); + background-image: -ms-linear-gradient(top, #eeeeee, #dedede); + background-image: -o-linear-gradient(top, #eeeeee, #dedede); + background-image: linear-gradient(to bottom, #eeeeee, #dedede); +} +.openerp .numeric { + text-align: right; + width: 82px; +} +.openerp .numeric input { + text-align: right; +} +.openerp ul, .openerp li, .openerp ol { + margin: 0; + padding: 0; +} +.openerp li { + list-style-type: none; +} +.openerp .oe_i { + font-family: "mnmliconsRegular" !important; + font-size: 21px; + font-weight: 300 !important; +} +.openerp .oe_left { + float: left; + margin-right: 8px; +} +.openerp .oe_right { + float: right; + margin-left: 8px; +} +.openerp .oe_clear { + clear: both; +} +.openerp .oe_wait { + cursor: wait; +} +.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp input[type='submit'] { + display: inline-block; + border: 1px solid #ababab; + color: #404040; + margin: 0; + padding: 4px 12px; + font-size: 13px; + text-align: center; + background-color: #efefef; + background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8)); + background-image: -webkit-linear-gradient(top, #efefef, #d8d8d8); + background-image: -moz-linear-gradient(top, #efefef, #d8d8d8); + background-image: -ms-linear-gradient(top, #efefef, #d8d8d8); + background-image: -o-linear-gradient(top, #efefef, #d8d8d8); + background-image: linear-gradient(to bottom, #efefef, #d8d8d8); + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); + -webkit-font-smoothing: antialiased; + outline: none; +} +.openerp a.button:hover, .openerp button:hover, .openerp input[type='submit']:hover { + background-color: #f6f6f6; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); + background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -moz-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -ms-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3); + cursor: pointer; +} +.openerp a.button:focus, .openerp button:focus, .openerp input[type='submit']:focus { + border: 1px solid #80bfff; + background-color: #f6f6f6; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); + background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -moz-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -ms-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3); + background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3); + -moz-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; + -webkit-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; + -box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; +} +.openerp a.button:active, .openerp a.button.active, .openerp button:active, .openerp button.active, .openerp input[type='submit']:active, .openerp input[type='submit'].active { + background: #e3e3e3; + background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f; + background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f; + background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp a.button.disabled, .openerp button:disabled, .openerp input[type='submit']:disabled { + background: #efefef !important; + border: 1px solid #d1d1d1 !important; + -moz-box-shadow: none !important; + -webkit-box-shadow: none !important; + -box-shadow: none !important; + color: #aaaaaa !important; + cursor: default; + text-shadow: 0 1px 1px white !important; +} +.openerp button.primary { + color: white; + background-color: #dc5f59; + background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)); + background-image: -webkit-linear-gradient(top, #dc5f59, #b33630); + background-image: -moz-linear-gradient(top, #dc5f59, #b33630); + background-image: -ms-linear-gradient(top, #dc5f59, #b33630); + background-image: -o-linear-gradient(top, #dc5f59, #b33630); + background-image: linear-gradient(to bottom, #dc5f59, #b33630); + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp button.primary:hover { + background-color: #df6b66; + background-image: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33)); + background-image: -webkit-linear-gradient(top, #df6b66, #bf3a33); + background-image: -moz-linear-gradient(top, #df6b66, #bf3a33); + background-image: -ms-linear-gradient(top, #df6b66, #bf3a33); + background-image: -o-linear-gradient(top, #df6b66, #bf3a33); + background-image: linear-gradient(to bottom, #df6b66, #bf3a33); + -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); + -box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); +} +.openerp button.primary:active { + background-color: #b33630; + background-image: -webkit-gradient(linear, left top, left bottom, from(#b33630), to(#dc5f59)); + background-image: -webkit-linear-gradient(top, #b33630, #dc5f59); + background-image: -moz-linear-gradient(top, #b33630, #dc5f59); + background-image: -ms-linear-gradient(top, #b33630, #dc5f59); + background-image: -o-linear-gradient(top, #b33630, #dc5f59); + background-image: linear-gradient(to bottom, #b33630, #dc5f59); + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp button.following { + color: white; + width: 120px; + background-color: #8a89ba; + background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4)); + background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4); + background-image: -moz-linear-gradient(top, #8a89ba, #807fb4); + background-image: -ms-linear-gradient(top, #8a89ba, #807fb4); + background-image: -o-linear-gradient(top, #8a89ba, #807fb4); + background-image: linear-gradient(to bottom, #8a89ba, #807fb4); +} +.openerp button.unfollow { + display: none; + color: white; + width: 120px; + background-color: #dc5f59; + background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)); + background-image: -webkit-linear-gradient(top, #dc5f59, #b33630); + background-image: -moz-linear-gradient(top, #dc5f59, #b33630); + background-image: -ms-linear-gradient(top, #dc5f59, #b33630); + background-image: -o-linear-gradient(top, #dc5f59, #b33630); + background-image: linear-gradient(to bottom, #dc5f59, #b33630); +} +.openerp .oe_loading { + display: none; + z-index: 100; + position: fixed; top: 0; - bottom: 0; + right: 50%; + padding: 4px 12px; + background: #a61300; + color: white; + text-align: center; + border: 1px solid #990000; + border-top: none; + -moz-border-radius-bottomright: 8px; + -moz-border-radius-bottomleft: 8px; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; +} +.openerp .oe_notification { + z-index: 1050; +} +.openerp .oe_login { + background: url("/web/static/src/img/pattern.png") repeat; + text-align: center; + font-size: 14px; + height: 100%; +} +.openerp .oe_login li { + list-style-type: none; + padding-bottom: 4px; +} +.openerp .oe_login button { + float: right; + display: inline-block; + cursor: pointer; + padding: 6px 16px; + border: 1px solid #222222; + color: white; + margin: 0; + background-color: #b92020; + background-image: -webkit-gradient(linear, left top, left bottom, from(#b92020), to(#600606)); + background-image: -webkit-linear-gradient(top, #b92020, #600606); + background-image: -moz-linear-gradient(top, #b92020, #600606); + background-image: -ms-linear-gradient(top, #b92020, #600606); + background-image: -o-linear-gradient(top, #b92020, #600606); + background-image: linear-gradient(to bottom, #b92020, #600606); + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; +} +.openerp .oe_login input, .openerp .oe_login select { + width: 252px; + font-family: "Lucida Grande", Helvetica, Verdana, Arial; + border: 1px solid #999999; + background: whitesmoke; + -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + -box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.openerp .oe_login input { + margin-bottom: 9px; + padding: 5px 6px; +} +.openerp .oe_login select { + padding: 1px; +} +.openerp .oe_login .oe_login_dbpane { + position: fixed; + top: 0; + right: 8px; + padding: 5px 10px; + color: #eeeeee; + border: solid 1px #333333; + background: #1e1e1e; + background: rgba(30, 30, 30, 0.94); + -moz-border-radius: 0 0 8px 8px; + -webkit-border-radius: 0 0 8px 8px; + border-radius: 0 0 8px 8px; +} +.openerp .oe_login .oe_login_dbpane input { + padding: 2px 4px; + margin: 4px 0; +} +.openerp .oe_login .oe_login_bottom { + position: absolute; + top: 50%; left: 0; right: 0; + bottom: 0; + text-shadow: 0 1px 1px #999999; + background-color: #b41616; + background-image: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606)); + background-image: -webkit-linear-gradient(top, #b41616, #600606); + background-image: -moz-linear-gradient(top, #b41616, #600606); + background-image: -ms-linear-gradient(top, #b41616, #600606); + background-image: -o-linear-gradient(top, #b41616, #600606); + background-image: linear-gradient(to bottom, #b41616, #600606); } -.openerp2 .oe_webclient .oe_application { +.openerp .oe_login .oe_login_pane { position: absolute; - top: 32px; - bottom: 0; - left: 206px; - right: 0; + top: 50%; + left: 50%; + margin: -160px -166px; + border: solid 1px #333333; + background: #1e1e1e; + background: rgba(30, 30, 30, 0.94); + padding: 22px 32px; + color: #eeeeee; + text-align: left; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; + -moz-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); + -webkit-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); + -box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); +} +.openerp .oe_login .oe_login_pane h2 { + margin-top: 0; + font-size: 18px; +} +.openerp .oe_login .oe_login_logo { + position: absolute; + top: -70px; + left: 0; + width: 100%; + margin: 0 auto; + text-align: center; +} +.openerp .oe_login .oe_login_footer { + position: absolute; + bottom: -40px; + left: 0; + width: 100%; + text-align: center; +} +.openerp .oe_login .oe_login_footer a { + color: #eeeeee; + margin: 0 8px; +} +.openerp .oe_login .oe_login_footer a:hover { + text-decoration: underline; +} +.openerp .oe_login .oe_login_footer span { + font-weight: bold; + font-size: 16px; +} +.openerp .oe_login .oe_login_error_message { + display: none; + background-color: #b41616; + color: #eeeeee; + padding: 14px 18px; + margin-top: 15px; + text-align: center; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); +} +.openerp .oe_login_invalid .oe_login_error_message { + display: inline-block; +} +.openerp .oe_database_manager { + background: white; + color: black; + text-align: left; +} +.openerp .oe_database_manager .oe_database_manager_menu { + color: black; } -.openerp2 .oe_content_full_screen .oe_application { +.openerp .oe_webclient { + width: 100%; + height: 100%; + border-spacing: 0px; +} +.openerp .oe_content_full_screen .oe_application { top: 0; left: 0; } -.openerp2 .oe_content_full_screen .topbar, .openerp2 .oe_content_full_screen .leftbar { +.openerp .oe_content_full_screen .oe_topbar, .openerp .oe_content_full_screen .oe_leftbar { display: none; } -.openerp2 .oe_topbar { +.openerp .oe_topbar { width: 100%; height: 31px; border-top: solid 1px #d3d3d3; @@ -44,17 +435,17 @@ background-image: -o-linear-gradient(top, #646060, #262626); background-image: linear-gradient(to bottom, #646060, #262626); } -.openerp2 .oe_topbar .oe_systray { +.openerp .oe_topbar .oe_systray { float: right; } -.openerp2 .oe_topbar .oe_systray > div { +.openerp .oe_topbar .oe_systray > div { float: left; padding: 0 4px 0 4px; } -.openerp2 .oe_topbar .oe_topbar_item li { +.openerp .oe_topbar .oe_topbar_item li { float: left; } -.openerp2 .oe_topbar .oe_topbar_item li a { +.openerp .oe_topbar .oe_topbar_item li a { display: block; padding: 5px 10px 7px; line-height: 20px; @@ -63,14 +454,14 @@ vertical-align: top; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } -.openerp2 .oe_topbar .oe_topbar_item li a:hover { +.openerp .oe_topbar .oe_topbar_item li a:hover { background: #303030; color: white; -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } -.openerp2 .oe_topbar .oe_topbar_item .oe_active { +.openerp .oe_topbar .oe_topbar_item .oe_active { background: #303030; font-weight: bold; color: white; @@ -78,7 +469,7 @@ -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } -.openerp2 .oe_topbar .oe_topbar_avatar { +.openerp .oe_topbar .oe_topbar_avatar { width: 24px; height: 24px; margin: -2px 2px 0 0; @@ -86,60 +477,134 @@ -webkit-border-radius: 4px; border-radius: 4px; } -.openerp2 .oe_topbar .oe_topbar_avatar { +.openerp .oe_topbar .oe_topbar_avatar { vertical-align: top; } -.openerp2 .oe_leftbar { - width: 205px; - height: 100%; +.openerp .oe_topbar .oe_dropdown_toggle:after { + width: 0; + height: 0; + display: inline-block; + content: "&darr"; + text-indent: -99999px; + vertical-align: top; + margin-top: 8px; + margin-left: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid white; + filter: alpha(opacity=50); + opacity: 0.5; +} +.openerp .oe_leftbar { + width: 220px; background: #f0eeee; border-right: 1px solid #afafb6; - overflow: auto; text-shadow: 0 1px 1px white; } -.openerp2 .oe_leftbar .oe_footer { - position: absolute; - width: 205px; - text-align: center; - bottom: 8px; -} -.openerp2 a.oe_logo { +.openerp a.oe_logo { + width: 220px; display: block; text-align: center; height: 70px; line-height: 70px; } -.openerp2 a.oe_logo img { +.openerp a.oe_logo img { height: 40px; width: 157px; margin: 14px 0; } -.openerp2 .oe_footer { - position: absolute; - width: 205px; +.openerp .oe_footer { + position: fixed; + bottom: 0; + padding: 4px 0; + background: #f0eeee; + width: 220px; text-align: center; - bottom: 8px; } -.openerp2 .oe_footer a { +.openerp .oe_footer a { font-weight: 800; font-family: serif; font-size: 16px; color: black; } -.openerp2 .oe_footer a span { +.openerp .oe_footer a span { color: #c81010; font-style: italic; } -.openerp2 .oe_menu { - float: left; +.openerp .oe_user_menu { + float: right; padding: 0; margin: 0; } -.openerp2 .oe_menu li { +.openerp .oe_user_menu li { list-style-type: none; float: left; } -.openerp2 .oe_menu a { +.openerp .oe_user_menu .oe_dropdown { + position: relative; +} +.openerp .oe_user_menu .oe_dropdown_options { + float: left; + background: #333333; + background: rgba(37, 37, 37, 0.9); + display: none; + position: absolute; + top: 32px; + right: -1px; + border: 0; + z-index: 900; + margin-left: 0; + margin-right: 0; + padding: 6px 0; + zoom: 1; + border-color: #999999; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 0 1px 1px; + -moz-border-radius: 0 0 6px 6px; + -webkit-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.openerp .oe_user_menu .oe_dropdown_options li { + float: none; + display: block; + background-color: none; +} +.openerp .oe_user_menu .oe_dropdown_options li a { + display: block; + padding: 4px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #eeeeee; +} +.openerp .oe_user_menu .oe_dropdown_options li a:hover { + background-color: #292929; + background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); + background-image: -webkit-linear-gradient(top, #292929, #191919); + background-image: -moz-linear-gradient(top, #292929, #191919); + background-image: -ms-linear-gradient(top, #292929, #191919); + background-image: -o-linear-gradient(top, #292929, #191919); + background-image: linear-gradient(to bottom, #292929, #191919); + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp .oe_menu { + float: left; + padding: 0; + margin: 0; +} +.openerp .oe_menu li { + float: left; +} +.openerp .oe_menu a { display: block; padding: 5px 10px 7px; line-height: 20px; @@ -148,14 +613,14 @@ vertical-align: top; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } -.openerp2 .oe_menu a:hover { +.openerp .oe_menu a:hover { background: #303030; color: white; -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } -.openerp2 .oe_menu .oe_active { +.openerp .oe_menu .oe_active { background: #303030; font-weight: bold; color: white; @@ -163,10 +628,10 @@ -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } -.openerp2 .oe_menu_more_container { +.openerp .oe_menu_more_container { position: relative; } -.openerp2 .oe_menu_more_container .oe_menu_more { +.openerp .oe_menu_more_container .oe_menu_more { position: absolute; padding: 0; background-color: #646060; @@ -175,34 +640,33 @@ border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } -.openerp2 .oe_menu_more_container .oe_menu_more li { +.openerp .oe_menu_more_container .oe_menu_more li { float: none; } -.openerp2 .oe_menu_more_container .oe_menu_more li a { +.openerp .oe_menu_more_container .oe_menu_more li a { white-space: nowrap; } -.openerp2 .oe_secondary_menu_section { +.openerp .oe_secondary_menu_section { font-weight: bold; margin-left: 8px; color: #8a89ba; } -.openerp2 .oe_secondary_submenu { +.openerp .oe_secondary_submenu { padding: 2px 0 8px 0; margin: 0; width: 100%; display: inline-block; } -.openerp2 .oe_secondary_submenu li { +.openerp .oe_secondary_submenu li { position: relative; padding: 1px 0 1px 16px; - list-style-type: none; } -.openerp2 .oe_secondary_submenu li a { +.openerp .oe_secondary_submenu li a { display: block; color: #4c4c4c; padding: 2px 4px 2px 0; } -.openerp2 .oe_secondary_submenu li .oe_menu_label { +.openerp .oe_secondary_submenu li .oe_menu_label { position: absolute; top: 1px; right: 1px; @@ -220,7 +684,20 @@ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); } -.openerp2 .oe_secondary_submenu .oe_active { +.openerp .oe_secondary_submenu .oe_menu_counter { + float: right; + background: #8a89ba; + color: #eeeeee; + font-size: 12px; + border: 1px solid lightgray; + padding: 0px 4px 0px 4px; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + margin: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.openerp .oe_secondary_submenu .oe_active { background: #8a89ba; border-top: 1px solid lightGray; border-bottom: 1px solid lightGray; @@ -229,10 +706,10 @@ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); } -.openerp2 .oe_secondary_submenu .oe_active a { +.openerp .oe_secondary_submenu .oe_active a { color: white; } -.openerp2 .oe_secondary_submenu .oe_active .oe_menu_label { +.openerp .oe_secondary_submenu .oe_active .oe_menu_label { background: #eeeeee; color: #8a89ba; text-shadow: 0 1px 1px white; @@ -240,7 +717,11 @@ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } -.openerp2 .oe_secondary_submenu .oe_menu_toggler:before { +.openerp .oe_secondary_submenu .oe_active .oe_menu_counter { + background: #eeeeee; + color: #8a89ba; +} +.openerp .oe_secondary_submenu .oe_menu_toggler:before { width: 0; height: 0; display: inline-block; @@ -256,7 +737,7 @@ filter: alpha(opacity=50); opacity: 0.5; } -.openerp2 .oe_secondary_submenu .oe_menu_opened:before { +.openerp .oe_secondary_submenu .oe_menu_opened:before { margin-top: 6px; margin-left: -12px; margin-right: 4px; @@ -264,83 +745,1075 @@ border-right: 4px solid transparent; border-top: 4px solid #4c4c4c; } -.openerp2 .oe_dropdown { +.openerp .oe_application { + width: 100%; +} +.openerp .oe_application a { + color: #8a89ba; +} +.openerp .oe_application a:hover { + text-decoration: underline; +} +.openerp .oe-view-manager-header { + border-top: 1px solid #cacaca; + border-bottom: 1px solid #cacaca; + background-color: #fcfcfc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); + background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede); + background-image: -moz-linear-gradient(top, #fcfcfc, #dedede); + background-image: -ms-linear-gradient(top, #fcfcfc, #dedede); + background-image: -o-linear-gradient(top, #fcfcfc, #dedede); + background-image: linear-gradient(to bottom, #fcfcfc, #dedede); + -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); + -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); +} +.openerp .oe-view-manager-header h2 { + float: left; + font-size: 18px; + margin: 1px 0; +} +.openerp .oe-view-manager-header h2 a { + color: #8a89ba; +} +.openerp .oe-view-manager-header .buttons { + padding-right: 8px; +} +.openerp .oe-view-manager-header .buttons li { + float: left; + margin-right: 12px; +} +.openerp .oe-view-manager-header .filter { + vertical-align: top; +} +.openerp .oe-view-manager-header .filter li { + height: 24px; + line-height: 24px; + padding: 0 8px; +} +.openerp .oe-view-manager-header .header-row { + height: 26px; + line-height: 26px; + margin: 8px; + clear: both; + text-shadow: 0 1px 1px white; +} +.openerp .oe-view-manager-header .header-row .dropdown-toggle:after { + margin-top: 7px; + margin-left: 6px; + border-top-color: #404040; +} +.openerp .oe-view-manager-header .oe_vm_switch li { + padding-left: 1px; + text-align: center; + width: 24px; + height: 24px; + line-height: 24px; +} +.openerp .oe-view-manager-header .pagination li { + height: 24px; + line-height: 24px; + padding: 0 8px; +} +.openerp .oe-view-manager-header .button-group { + display: inline-block; + border: 1px solid #ababab; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.openerp .oe-view-manager-header .button-group li { + float: left; + border-right: 1px solid #ababab; +} +.openerp .oe-view-manager-header .button-group li:last-child { + border: none; +} +.openerp .oe-view-manager-header .button-group a { + color: #4c4c4c; +} +.openerp .oe-view-manager-header .button-group a:hover { + text-decoration: none; +} +.openerp .oe-view-manager-header .button-group .active { + width: 100%; + background: #999999; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); +} +.openerp .oe_view_manager_header2 { + width: 100%; + border-top: 1px solid #cacaca; + border-bottom: 1px solid #cacaca; + background-color: #fcfcfc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); + background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede); + background-image: -moz-linear-gradient(top, #fcfcfc, #dedede); + background-image: -ms-linear-gradient(top, #fcfcfc, #dedede); + background-image: -o-linear-gradient(top, #fcfcfc, #dedede); + background-image: linear-gradient(to bottom, #fcfcfc, #dedede); + -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); + -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1); +} +.openerp .oe_view_manager_header2 h2 { + float: left; + font-size: 18px; + margin: 1px 0; +} +.openerp .oe_view_manager_header2 h2 a { + color: #8a89ba; +} +.openerp .oe_view_manager_header2 td { + padding: 4px 8px; + line-height: 26px; +} +.openerp .oe_view_manager_header2 .oe_button_group { + display: inline-block; + border: 1px solid #ababab; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.openerp .oe_view_manager_header2 .oe_button_group li { + float: left; + border-right: 1px solid #ababab; +} +.openerp .oe_view_manager_header2 .oe_button_group li:last-child { + border: none; +} +.openerp .oe_view_manager_header2 .oe_button_group a { + color: #4c4c4c; +} +.openerp .oe_view_manager_header2 .oe_button_group a:hover { + text-decoration: none; +} +.openerp .oe_view_manager_header2 .oe_button_group .active { + background: #999999; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; +} +.openerp .oe_view_manager_header2 .oe_button_group .active a { + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); +} +.openerp .oe_view_manager_header2 .oe_view_manager_buttons { + white-space: nowrap; +} +.openerp .oe_view_manager_body h4 { + margin: 8px 0; +} +.openerp .oe_pager_value { + float: left; + margin-right: 8px; +} +.openerp .oe_pager_group { + float: left; + height: 24px; + line-height: 24px; + display: inline-block; + border: 1px solid #ababab; + cursor: pointer; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.openerp .oe_pager_group li { + height: 24px; + line-height: 24px; + padding: 0; + float: left; + border-right: 1px solid #ababab; +} +.openerp .oe_pager_group li:last-child { + border: none; +} +.openerp .oe_pager_group a { + color: #4c4c4c; + padding: 0 8px; +} +.openerp .oe_pager_group a:hover { + text-decoration: none; +} +.openerp .oe_pager_group .active { + background: #999999; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset; +} +.openerp .oe_pager_group .active a { + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); +} +.openerp .oe_view_manager_switch li { + padding-left: 1px; + text-align: center; + width: 24px; + height: 24px; + line-height: 24px; +} +.openerp .oe_view_manager_switch .oe_vm_switch_list:after, .openerp .oe_view_manager_switch .oe_vm_switch_tree:after { + content: "ö"; +} +.openerp .oe_view_manager_switch .oe_vm_switch_graph:after { + content: "ó"; +} +.openerp .oe_view_manager_switch .oe_vm_switch_gantt:after { + content: "y"; +} +.openerp .oe_view_manager_switch .oe_vm_switch_calendar:after { + content: "b"; +} +.openerp .oe_view_manager_switch .oe_vm_switch_kanban:after { + content: "ó"; +} +.openerp .oe_view_manager_switch .oe_vm_switch_diagram:after { + content: "}"; +} +.openerp .oe_form_dropdown_section { position: relative; + display: inline-block; + margin-right: 4px; } -.openerp2 .oe_dropdown_toggle:after { +.openerp .oe_dropdown_toggle:after { width: 0; height: 0; display: inline-block; content: "&darr"; text-indent: -99999px; vertical-align: top; - margin-top: 8px; - margin-left: 4px; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid white; filter: alpha(opacity=50); opacity: 0.5; + margin-top: 7px; + margin-left: 6px; + border-top-color: #404040; } -.openerp2 .oe_user_menu { - float: right; - padding: 0; - margin: 0; -} -.openerp2 .oe_user_menu li { - list-style-type: none; - float: left; -} -.openerp2 .oe_user_menu .oe_dropdown_options { - float: left; - background: #333333; - background: rgba(37, 37, 37, 0.9); +.openerp .oe_dropdown_menu { display: none; position: absolute; - top: 32px; - right: -1px; - border: 0; + top: 28px; + left: 0px; + padding: 8px; + border: 1px solid #afafb6; + width: 120px; + overflow-x: hidden; z-index: 900; - margin-left: 0; - margin-right: 0; - padding: 6px 0; - zoom: 1; - border-color: #999999; - border-color: rgba(0, 0, 0, 0.2); - border-style: solid; - border-width: 0 1px 1px; - -moz-border-radius: 0 0 6px 6px; - -webkit-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; + text-align: left; + background: white; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; } -.openerp2 .oe_user_menu .oe_dropdown_options li { +.openerp .oe_dropdown_menu li { + list-style-type: none; float: none; display: block; background-color: none; } -.openerp2 .oe_user_menu .oe_dropdown_options li a { +.openerp .oe_dropdown_menu li a { display: block; - padding: 4px 15px; + padding: 3px 6px; clear: both; font-weight: normal; + line-height: 14px; + color: #4c4c4c; + text-decoration: none; +} +.openerp .oe_dropdown_menu li a:hover { + text-decoration: none; + background-color: #f0f0fa; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6)); + background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6); + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp .oe_sidebar { + white-space: nowrap; +} +.openerp .oe_searchview { + position: relative; + float: right; +} +.openerp .oe_searchview .VS-search .VS-search-box { + min-height: 0; + border-width: 1px; + padding: 0; + width: 410px; +} +.openerp .oe_searchview .VS-search .VS-icon { + top: 5px; +} +.openerp .oe_searchview .VS-search .VS-icon-search { + left: 5px; +} +.openerp .oe_searchview .VS-search .VS-search-inner { + margin: 0 40px 0 17px; + font-size: 13px; +} +.openerp .oe_searchview .VS-search .VS-search-inner input { + font-size: inherit; + line-height: inherit; + height: auto; + padding: 0; +} +.openerp .oe_searchview .VS-search .VS-search-inner .VS-input-width-tester { + font-size: inherit; + padding: 0; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_input { + margin-left: 0; + height: 22px; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_input input, .openerp .oe_searchview .VS-search .VS-search-inner .search_input .VS-input-width-tester { + padding: 0 2px; + height: inherit; + margin: 0; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet { + padding: 0; + margin: 1px 0; + border: 1px solid #afafb6; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: #f0f0fa; + height: 18px; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected { + border-color: #a6a6fe; + -moz-box-shadow: 0 0 3px 1px #a6a6fe; + -webkit-box-shadow: 0 0 3px 1px #a6a6fe; + -box-shadow: 0 0 3px 1px #a6a6fe; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .category { + margin-left: 0; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .VS-icon-cancel { + filter: alpha(opacity=100); + opacity: 1; + background-position: center 0; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .category { + padding: 0 4px 0 14px; + color: white; + background: #8786b7; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4); + font-weight: normal; + text-transform: none; + height: 18px; line-height: 18px; - color: #eeeeee; + font-size: inherit; } -.openerp2 .oe_user_menu .oe_dropdown_options li a:hover { - background-color: #292929; - background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); - background-image: -webkit-linear-gradient(top, #292929, #191919); - background-image: -moz-linear-gradient(top, #292929, #191919); - background-image: -ms-linear-gradient(top, #292929, #191919); - background-image: -o-linear-gradient(top, #292929, #191919); - background-image: linear-gradient(to bottom, #292929, #191919); +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_input_container { + border-left: 1px solid #afafb6; + height: 18px; + line-height: 18px; + padding: 0 4px; + cursor: pointer; +} +.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_remove { + left: 1px; + top: 3px; +} +.openerp .oe_searchview .VS-search .VS-icon-cancel { + right: 24px; +} +.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer { + position: absolute; + top: 0; + right: 0; + height: 100%; + line-height: 23px; + padding: 0 7px 0 4px; + color: #cccccc; + cursor: pointer; +} +.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer:before { + content: "▾"; +} +.openerp .oe_searchview.oe_searchview_open_drawer .oe_searchview_drawer { + display: block; +} +.openerp .oe_searchview .oe_searchview_drawer { + position: absolute; + z-index: 1; + top: 100%; + right: 0; + background-color: white; + width: 410px; + display: none; + border: 1px solid #cccccc; + text-align: left; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters { + display: table; + width: 100%; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters > div { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: table-cell; + width: 50%; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters h3 { + margin: 2px 4px 2px 8px; + color: #8786b7; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters ul { + margin: 0 12px 3px; + padding: 0; + list-style: none; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li { + list-style: none; + padding: 3px 6px; + height: 14px; + color: inherit; + cursor: pointer; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li:hover { + background-color: #f0f0fa; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4 { + border-top: 1px solid #cccccc; + margin: 5px 0 3px; + cursor: pointer; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4:before { + content: "▸ "; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced div { + display: none; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_add_condition:before { + content: "⊞ "; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_apply:before { + content: "⚡ "; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced ul { + list-style: none; + padding: 0; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li { + list-style: none; + margin: 0; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened h4:before { + content: "▾ "; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened div { + display: block; +} +.openerp .oe_view_topbar { + border-bottom: 1px solid #cacaca; + background-color: #fcfcfc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); + background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede); + background-image: -moz-linear-gradient(top, #fcfcfc, #dedede); + background-image: -ms-linear-gradient(top, #fcfcfc, #dedede); + background-image: -o-linear-gradient(top, #fcfcfc, #dedede); + background-image: linear-gradient(to bottom, #fcfcfc, #dedede); + padding: 0 8px; + line-height: 30px; +} +.openerp .oe_formview { + background: white; +} +.openerp .oe_form_header { + padding: 8px; +} +.openerp .oe_form_invalid input, .openerp .oe_form_invalid select, .openerp .oe_form_invalid textarea { + background-color: #ff6666 !important; + border: 1px solid #dd0000 !important; +} +.openerp .oe_form_button_save_dirty { + color: white; + background: #dc5f59; + background: -moz-linear-gradient(#dc5f59, #b33630); + background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)); + background: -webkit-linear-gradient(#dc5f59, #b33630); -moz-box-shadow: none; -webkit-box-shadow: none; -box-shadow: none; + font-weight: bold; +} +.openerp .oe_form_button_save_dirty:hover { + background: #ed6f6a; +} +.openerp .oe_form_topbar { + border-bottom: 1px solid #cacaca; + background-color: #fcfcfc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); + background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede); + background-image: -moz-linear-gradient(top, #fcfcfc, #dedede); + background-image: -ms-linear-gradient(top, #fcfcfc, #dedede); + background-image: -o-linear-gradient(top, #fcfcfc, #dedede); + background-image: linear-gradient(to bottom, #fcfcfc, #dedede); + padding: 0 8px; + line-height: 30px; +} +.openerp .oe_form_topbar button { + font-size: 12px; + height: 24px; + line-height: 24px; + vertical-align: top; + padding: 0 10px; + margin: 3px 0; +} +.openerp .oe_form_topbar button span.i { + line-height: 20px; + height: 24px; +} +.openerp .oe_form_topbar ul { + height: 30px; + padding: 0; + margin: 0; + text-shadow: 0 1px 1px white; + border-left: 1px solid #cacaca; + border-right: 1px solid #cacaca; +} +.openerp .oe_form_topbar ul li { + padding: 0; + margin: 0; + float: left; + vertical-align: top; + border-right: 1px solid #cacaca; + height: 30px; + padding: 0 12px; +} +.openerp .oe_form_topbar ul li:first-child { + border-left: 1px solid #cacaca; +} +.openerp .oe_form_topbar ul li a { + color: #4c4c4c; +} +.openerp .oe_form_topbar ul li a:hover { + color: black; +} +.openerp .oe_form_topbar .oe_form_steps img { + margin: 0 8px; + vertical-align: top; +} +.openerp .oe_form_topbar .oe_form_steps li { + border-right: none; + padding: 0; +} +.openerp .oe_form_topbar .oe_form_steps li:first-child { + margin-left: 12px; + border-left: none; +} +.openerp .oe_form_topbar .oe_form_steps li:last-child { + margin-right: 12px; +} +.openerp .oe_form_topbar .oe_form_steps_active { + font-weight: bold; + color: #b33630; +} +.openerp .oe_application .oe_form_sheetbg { + background: url(/web/static/src/img/form_sheetbg.png); + padding: 8px; + border-bottom: 1px solid #dddddd; +} +.openerp .oe_application .oe_form_sheet_width { + min-width: 650px; + max-width: 980px; + margin: 0 auto; +} +.openerp .oe_application .oe_form_sheet { + padding: 8px 8px 8px 8px; + background: white; + min-height: 420px; + border: 1px solid #afafb6; + -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + -box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} +.openerp .oe_form_autolayout .oe_button { + height: 22px; +} +.openerp .oe_form_autolayout .oe_form_group_cell { + line-height: 18px; + padding: 2px; + height: 25px; +} +.openerp .oe_form_autolayout .oe_form_group_cell .oe_form_button { + width: 100%; + white-space: nowrap; +} +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_many2one, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_many2one input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_char input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_text textarea, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_email, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_email input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_url, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_url input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_date input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_float input, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_form_field_selection select, +.openerp .oe_form_autolayout .oe_form_group_cell > .oe_datepicker_root { + width: 100%; +} +.openerp .oe_form_autolayout .oe_form_button.oe_button { + color: #4c4c4c; + white-space: nowrap; + min-width: 100%; + width: 100%; + height: 22px; +} +.openerp .oe_form_autolayout .oe_form_button.oe_button span { + position: relative; + vertical-align: top; +} +.openerp .oe_form_autolayout .oe_form_group_cell.oe_form_group_nested { + padding: 0; +} +.openerp .oe_form label { + font-weight: bold; +} +.openerp .oe_form_label_help[for], .openerp .oe_form_label[for] { + white-space: nowrap; +} +.openerp .oe_form textarea { + resize: vertical; +} +.openerp .oe_form input[type="text"], +.openerp .oe_form input[type="password"], +.openerp .oe_form input[type="file"], +.openerp .oe_form select { + height: 22px; + padding-top: 2px; +} +.openerp .oe_form input[type="text"], +.openerp .oe_form input[type="password"], +.openerp .oe_form input[type="file"], +.openerp .oe_form select, +.openerp .oe_form textarea { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + padding: 0 2px; + margin: 0 2px; + border: 1px solid #999999; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: white; + min-width: 90px; + color: #1f1f1f; +} +.openerp .oe_form input[readonly], +.openerp .oe_form select[readonly], +.openerp .oe_form textarea[readonly], +.openerp .oe_form input[disabled], +.openerp .oe_form select[disabled], +.openerp .oe_form textarea[disabled] { + background: #e5e5e5 !important; + color: #666666; +} +.openerp .oe_form .oe_form_field_many2one input, +.openerp .oe_form .oe_form_field_binary input, +.openerp .oe_form .oe_form_field_binary input, +.openerp .oe_form .oe_form_field_email input, +.openerp .oe_form .oe_form_field_url input { + border-right: none; + -webkit-border-top-right-radius: 0px; + -webkit-border-bottom-right-radius: 0px; + -moz-border-radius-topright: 0px; + -moz-border-radius-bottomright: 0px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; +} +.openerp .oe_form .oe_form_field_email button img, +.openerp .oe_form .oe_form_field_url button img { + vertical-align: top; +} +.openerp .oe_form .oe_form_field_translatable, +.openerp .oe_form .oe_form_field_many2one, +.openerp .oe_form .oe_form_field_date, +.openerp .oe_form .oe_form_field_datetime { + white-space: nowrap; +} +.openerp .oe_form .oe_form_field_boolean { + padding-top: 4px; +} +.openerp .oe_form .oe_form_field_datetime input { + min-width: 11em; +} +.openerp .oe_form .oe_datepicker_container { + display: none; +} +.openerp .oe_form .oe_datepicker_root { + display: inline-block; +} +.openerp .oe_form .oe_form_required input, .openerp .oe_form .oe_form_required select, .openerp .oe_form .oe_form_required textarea { + background-color: #d2d2ff !important; +} +.openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea { + background-color: #ff6666 !important; + border: 1px solid #dd0000 !important; +} +.openerp .oe_form .oe_button.oe_field_button { + -webkit-border-top-left-radius: 0px; + -webkit-border-bottom-left-radius: 0px; + -moz-border-radius-topleft: 0px; + -moz-border-radius-bottomleft: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + margin-right: -1px; + height: 22px; +} +.openerp .oe_form .oe_input_icon { + cursor: pointer; + margin: 3px 0 0 -21px; + vertical-align: top; +} +.openerp .oe_form .oe_input_icon_disabled { + position: absolute; + cursor: default; + opacity: 0.5; + filter: alpha(opacity=50); + right: 5px; + top: 3px; +} +.openerp .oe_form .oe-binary-file-set { + overflow: hidden; + position: relative; + width: 45px; + height: 30px; +} +.openerp .oe_form input.oe-binary-file { + z-index: 0; + line-height: 0; + font-size: 12px; + position: absolute; + top: 1px; + right: 10px; + opacity: 0; + filter: alpha(opacity=0); + -ms-filter: "alpha(opacity=0)"; + margin: 0; + padding: 0; +} +.openerp .oe_horizontal_separator { + font-weight: bold; + border: 0 solid #666666; + border-bottom-width: 1px; + margin: 3px 4px 3px 1px; + height: 17px; +} +.openerp .oe_horizontal_separator:empty { + height: 5px; +} +.openerp .oe_vertical_separator { + border-left: 1px solid #666666; + padding: 0 4px 0 4px; +} +.openerp .oe_form_notebook { + margin: 8px 0; + padding: 0 8px; + list-style: none; + zoom: 1; +} +.openerp .oe_form_notebook:before, .openerp .oe_form_notebook:after { + display: table; + content: ""; + zoom: 1; +} +.openerp .oe_form_notebook:after { + clear: both; +} +.openerp .oe_form_notebook > li { + float: left; +} +.openerp .oe_form_notebook > li > a { + display: block; + color: #4c4c4c; +} +.openerp .oe_form_notebook { + border-color: #dddddd; + border-style: solid; + border-width: 0 0 1px; +} +.openerp .oe_form_notebook > li { + position: relative; + margin-bottom: -1px; +} +.openerp .oe_form_notebook > li > a { + padding: 0 12px; + margin-right: 2px; + line-height: 30px; + border: 1px solid transparent; + -moz-border-radius: 4px 4px 0 0; + -webkit-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.openerp .oe_form_notebook > li > a:hover { + text-decoration: none; + background-color: #eeeeee; + border-color: #eeeeee #eeeeee #dddddd; +} +.openerp .oe_form_notebook > li.ui-state-active > a, .openerp .oe_form_notebook > li.ui-state-active > a:hover { + background-color: white; + border: 1px solid #dddddd; + border-bottom-color: transparent; + cursor: default; +} +.openerp .ui-tabs-hide { + display: none; +} +.openerp .oe_form .oe_form_field_progressbar.ui-progressbar { + height: 22px; + font-size: 10px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + border: 1px solid #999999; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: white; + min-width: 90px; +} +.openerp .oe_form .oe_form_field_progressbar.ui-progressbar span { + position: absolute; + margin-left: 10px; + font-weight: bold; +} +.openerp .oe_form .oe_form_field_progressbar.ui-progressbar .ui-widget-header { + background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; +} +.openerp .oe_form .oe_view_manager_header2 td { + padding: 0px 8px; + line-height: 16px; +} +.openerp .oe_form .oe_view_manager_header2 td .oe_i { + font-size: 13px; +} +.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group { + height: auto; + line-height: 16px; +} +.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group li { + height: auto; + line-height: 16px; +} +.openerp .oe_listview_nocontent > img { + float: left; + margin-right: 1.5em; +} +.openerp .oe_listview_nocontent > div { + overflow: hidden; + padding: 6px; + font-size: 125%; +} +.openerp .oe-listview-content { + width: 100%; +} +.openerp .oe-listview-content thead, .openerp .oe-listview-content tfoot { + border-bottom: 1px solid #cacaca; + background: #eeeeee; +} +.openerp .oe-listview-content tfoot { + border-bottom: 1px solid #cacaca; + font-weight: bold; +} +.openerp .oe-listview-content tbody { + cursor: pointer; + background: white; +} +.openerp .oe-listview-content thead { + vertical-align: top; +} +.openerp .oe-listview-content td.oe_list_checkbox:first-child, .openerp .oe-listview-content th.oe_list_checkbox:first-child { + width: 17px; +} +.openerp .oe-listview-content td.oe_list_checkbox:first-child:after, .openerp .oe-listview-content th.oe_list_checkbox:first-child:after { + border-width: 0; +} +.openerp .oe-listview-content tbody tr:nth-child(odd) td, .openerp .oe-listview-content tbody tr:nth-child(odd) th { + background-color: #f0f0fa; + background-color: #f0f0fa; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6)); + background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6); + background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6); +} +.openerp .oe-listview-content tbody tr:hover td, .openerp .oe-listview-content tbody tr:hover th { + background-color: #eeeeee; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#dedede)); + background-image: -webkit-linear-gradient(top, #eeeeee, #dedede); + background-image: -moz-linear-gradient(top, #eeeeee, #dedede); + background-image: -ms-linear-gradient(top, #eeeeee, #dedede); + background-image: -o-linear-gradient(top, #eeeeee, #dedede); + background-image: linear-gradient(to bottom, #eeeeee, #dedede); +} +.openerp .oe-listview-content td, .openerp .oe-listview-content th { + padding: 3px 6px; + line-height: 18px; +} +.openerp .oe-listview-content td button, .openerp .oe-listview-content th button { + border: none; + background: transparent; + padding: 0; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; +} +.openerp .oe-listview-content .numeric { + text-align: right; + width: 82px; +} +.openerp .oe-listview-content .numeric input { + text-align: right; +} +.openerp .oe-listview-content table .sortdown:after, .openerp .oe-listview-content table .sortdown:hover:after { + visibility: visible; + filter: alpha(opacity=60); + opacity: 0.6; +} +.openerp .oe-listview-content table .sortup:after { + border-bottom: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid black; + visibility: visible; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; + filter: alpha(opacity=60); + opacity: 0.6; +} +.openerp .oe_kanban_group_title { + margin: 1px 1px 4px; + font-size: 15px; + font-weight: bold; + text-shadow: 0 1px 0 white; +} +.openerp .oe_kanban_column, .openerp .oe_kanban_group_header { + width: 240px; + vertical-align: top; + padding: 6px 7px 6px 6px; + background: #f0eeee; + border-left: 1px solid #f0f8f8; + border-right: 1px solid #b9b9b9; +} +.openerp .oe_kanban_record { + position: relative; + display: block; + min-height: 50px; + margin: 6px 0; + display: block; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.openerp .oe_kanban_record:last-child { + margin-bottom: 0; +} +.openerp .oe_kanban_record .oe_kanban_title { + font-weight: bold; + margin: 2px 4px; +} +.openerp .oe_kanban_gravatar { + width: 20px; + height: 20px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.openerp .oe_kanban_avatar_toto { + height: 40px; + width: 40px; + border: 1px solid; + border-color: #e5e5e5 #dbdbdb #d2d2d2; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.openerp .oe_trad_field.touched { + border: 1px solid green !important; +} +.openerp.oe_tooltip { + font-size: 12px; +} +.openerp.oe_tooltip .oe_tooltip_string { + color: #ffdd55; + font-weight: bold; + font-size: 13px; +} +.openerp.oe_tooltip .oe_tooltip_help { + white-space: pre-wrap; +} +.openerp.oe_tooltip .oe_tooltip_technical { + padding: 0 0 4px 0; + margin: 5px 0 0 15px; +} +.openerp.oe_tooltip .oe_tooltip_technical li { + list-style: circle; +} +.openerp.oe_tooltip .oe_tooltip_technical_title { + font-weight: bold; +} +.openerp .oe_layout_debugging .oe_form_group { + outline: 2px dashed red; +} +.openerp .oe_layout_debugging .oe_form_group_cell { + outline: 1px solid blue; +} +.openerp .oe_debug_view { + float: left; +} +.openerp .oe_debug_view_log { + font-size: 95%; +} +.openerp .oe_debug_view_log label { + display: block; + width: 49%; + text-align: right; + float: left; + font-weight: bold; + color: #000099; +} +.openerp .oe_debug_view_log span { + display: block; + width: 49%; + float: right; + color: #333333; +} + +.openerp .oe_form_field_many2one td:first-child { + position: relative; +} +.openerp .oe_form_field_many2one span.oe-m2o-drop-down-button { + position: absolute; + top: 3px; + right: 2px; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index c31f501c499..98caeafbb29 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1,9 +1,21 @@ +@charset "utf-8" + // Variables {{{ +$section-title-color: #8786b7 +$facets-border: #afafb6 +$facets-border-selected: #a6a6fe $colour4: #8a89ba -//$colour4: #d14836 // }}} - // Mixins {{{ +@font-face + font-family: 'mnmliconsRegular' + src: url('/web/static/src/font/mnmliconsv21-webfont.eot') format('eot') + src: url('/web/static/src/font/mnmliconsv21-webfont.woff') format('woff') + src: url('/web/static/src/font/mnmliconsv21-webfont.ttf') format('truetype') + src: url('/web/static/src/font/mnmliconsv21-webfont.svg') format('svg') active + font-weight: normal + font-style: normal + @mixin vertical-gradient($startColor: #555, $endColor: #333) background-color: $startColor background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */ @@ -46,48 +58,295 @@ $colour4: #8a89ba -webkit-background-clip: $clip -moz-background-clip: $clip background-clip: $clip + +@mixin unscrew-box + -moz-box-sizing: border-box + -webkit-box-sizing: border-box + box-sizing: border-box + // }}} -.openerp2 - // Common styles {{{ +.openerp.openerp-web-client-container + height: 100% + position: relative + +.openerp + // Global style {{{ padding: 0 margin: 0 font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif color: #4c4c4c font-size: 13px background: white - position: relative - + // }}} + // Tag reset {{{ a text-decoration: none + table + padding: 0 + font-size: 13px + border-collapse: collapse + thead + font-weight: bold + background-color: #f0f0f0 + th + border-right: 1px dotted #afafb6 + &:last-child + border-right: none + th, td + padding: 0 + line-height: 18px + text-align: left + th + font-weight: bold + vertical-align: middle + td + vertical-align: top + .zebra tbody tr:nth-child(odd) td + background-color: #f0f0fa + @include vertical-gradient(#f0f0fa, #eeeef6) + .zebra tbody tr:hover td + @include vertical-gradient(#eee, #dedede) + .numeric + text-align: right + width: 82px + input + text-align: right + ul, li, ol + margin: 0 + padding: 0 + li + list-style-type: none // }}} + // Generic classes {{{ + .oe_i + font-family: "mnmliconsRegular" !important + font-size: 21px + font-weight: 300 !important + .oe_left + float: left + margin-right: 8px + .oe_right + float: right + margin-left: 8px + .oe_clear + clear: both + .oe_wait + cursor: wait + // }}} + // Button style {{{ + a.button:link, a.button:visited, button, input[type='submit'] + display: inline-block + border: 1px solid #ababab + color: #404040 + margin: 0 + padding: 4px 12px + font-size: 13px + text-align: center + @include vertical-gradient(#efefef, #d8d8d8) + @include radius(3px) + @include box-shadow((0 1px 2px rgba(0, 0, 0, .1), 0 1px 1px rgba(255, 255, 255, .8) inset)) + text-shadow: 0 1px 1px rgba(255, 255, 255, .5) + -webkit-font-smoothing: antialiased + outline: none - // WebClient {{{ - .oe_webclient - position: absolute - top: 0 - bottom: 0 - left: 0 - right: 0 + a.button:hover, button:hover, input[type='submit']:hover + @include vertical-gradient(#f6f6f6, #e3e3e3) + cursor: pointer - .oe_application + a.button:focus, button:focus, input[type='submit']:focus + border: 1px solid #80bfff + @include vertical-gradient(#f6f6f6, #e3e3e3) + @include box-shadow((0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, .8) inset)) + + a.button:active, a.button.active, button:active, button.active, input[type='submit']:active, input[type='submit'].active + background: #e3e3e3 + background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f + background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f + background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f + @include box-shadow(none) + + a.button.disabled, button:disabled, input[type='submit']:disabled + background: #efefef !important + border: 1px solid #d1d1d1 !important + @include box-shadow(none !important) + color: #aaa !important + cursor: default + text-shadow: 0 1px 1px #fff !important + + button.primary + color: white + @include vertical-gradient(#dc5f59, #b33630) + @include box-shadow(none) + + button.primary:hover + @include vertical-gradient(lighten(#dc5f59, 3%), lighten(#b33630, 3%)) + @include box-shadow(0 0 1px rgba(0,0,0,0.2)) + + button.primary:active + @include vertical-gradient(#b33630, #dc5f59) + @include box-shadow(none) + + button.following + color: white + width: 120px + @include vertical-gradient($colour4, darken($colour4, 3%)) + + button.unfollow + display: none + color: white + width: 120px + @include vertical-gradient(#dc5f59, #b33630) + + // }}} + // Loading {{{ + .oe_loading + display: none + z-index: 100 + position: fixed + top: 0 + right: 50% + padding: 4px 12px + background: #A61300 + color: white + text-align: center + border: 1px solid #900 + border-top: none + -moz-border-radius-bottomright: 8px + -moz-border-radius-bottomleft: 8px + border-bottom-right-radius: 8px + border-bottom-left-radius: 8px + // }}} + // Notification {{{ + .oe_notification + z-index: 1050 + // }}} + // Login {{{ + .oe_login + background: url("/web/static/src/img/pattern.png") repeat + text-align: center + font-size: 14px + height: 100% + li + list-style-type: none + padding-bottom: 4px + button + float: right + display: inline-block + cursor: pointer + padding: 6px 16px + border: 1px solid #222 + color: white + margin: 0 + @include vertical-gradient(#b92020, #600606) + @include radius(4px) + @include box-shadow((0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset)) + input, select + width: 252px + font-family: "Lucida Grande", Helvetica, Verdana, Arial + border: 1px solid #999999 + background: whitesmoke + @include box-shadow(inset 0 1px 4px rgba(0, 0, 0, 0.3)) + @include radius(3px) + input + margin-bottom: 9px + padding: 5px 6px + select + padding: 1px + .oe_login_dbpane + position: fixed + top: 0 + right: 8px + padding: 5px 10px + color: #eee + border: solid 1px #333 + background: #1e1e1e + background: rgba(30,30,30,0.94) + @include radius(0 0 8px 8px) + input + padding: 2px 4px + margin: 4px 0 + .oe_login_bottom position: absolute - top: 32px - bottom: 0 - left: 206px + top: 50% + left: 0 right: 0 + bottom: 0 + text-shadow: 0 1px 1px #999999 + @include vertical-gradient(#b41616, #600606) + .oe_login_pane + position: absolute + top: 50% + left: 50% + margin: -160px -166px + border: solid 1px #333333 + background: #1e1e1e + background: rgba(30,30,30,0.94) + padding: 22px 32px + color: #eee + text-align: left + @include radius(8px) + @include box-shadow(0 0 18px rgba(0, 0, 0, 0.9)) + h2 + margin-top: 0 + font-size: 18px + .oe_login_logo + position: absolute + top: -70px + left: 0 + width: 100% + margin: 0 auto + text-align: center + .oe_login_footer + position: absolute + bottom: -40px + left: 0 + width: 100% + text-align: center + a + color: #eee + margin: 0 8px + &:hover + text-decoration: underline + span + font-weight: bold + font-size: 16px + .oe_login_error_message + display: none + background-color: #b41616 + color: #eee + padding: 14px 18px + margin-top: 15px + text-align: center + @include radius(4px) + @include box-shadow(0 1px 4px rgba(0, 0, 0, 0.8)) + .oe_login_invalid + .oe_login_error_message + display: inline-block // }}} - - // Fullscreen mode {{{ + // DatabaseManager {{{ + .oe_database_manager + background: #fff + color: #000 + text-align: left + .oe_database_manager_menu + color: #000 + // }}} + // WebClient {{{ + .oe_webclient + width: 100% + height: 100% + border-spacing: 0px + // }}} + // WebClient.fullscreen {{{ .oe_content_full_screen .oe_application top: 0 left: 0 - .topbar, .leftbar + .oe_topbar, .oe_leftbar display: none - // }} - - // Topbar {{{ + // }}} + // WebClient.topbar {{{ .oe_topbar width: 100% height: 31px @@ -129,23 +388,28 @@ $colour4: #8a89ba @include radius(4px) .oe_topbar_avatar vertical-align: top + .oe_dropdown_toggle:after + width: 0 + height: 0 + display: inline-block + content: "&darr" + text-indent: -99999px + vertical-align: top + margin-top: 8px + margin-left: 4px + border-left: 4px solid transparent + border-right: 4px solid transparent + border-top: 4px solid white + @include opacity(0.5) // }}} - - // Leftbar {{{ + // Webclient.leftbar {{{ .oe_leftbar - width: 205px - height: 100% + width: 220px background: #f0eeee border-right: 1px solid #afafb6 - overflow: auto text-shadow: 0 1px 1px white - - .oe_footer - position: absolute - width: 205px - text-align: center - bottom: 8px a.oe_logo + width: 220px display: block text-align: center height: 70px @@ -155,10 +419,12 @@ $colour4: #8a89ba width: 157px margin: 14px 0 .oe_footer - position: absolute - width: 205px + position: fixed + bottom: 0 + padding: 4px 0 + background: #f0eeee + width: 220px text-align: center - bottom: 8px a font-weight: 800 font-family: serif @@ -168,14 +434,58 @@ $colour4: #8a89ba color: #c81010 font-style: italic // }}} - + // UserMenu {{{ + .oe_user_menu + float: right + padding: 0 + margin: 0 + li + list-style-type: none + float: left + .oe_dropdown + position: relative + .oe_dropdown_options + float: left + background: #333 + background: rgba(37,37,37,0.9) + display: none + position: absolute + top: 32px + right: -1px + border: 0 + z-index: 900 + margin-left: 0 + margin-right: 0 + padding: 6px 0 + zoom: 1 + border-color: #999 + border-color: rgba(0, 0, 0, 0.2) + border-style: solid + border-width: 0 1px 1px + @include radius(0 0 6px 6px) + @include box-shadow(0 1px 4px rgba(0,0,0,0.3)) + @include background-clip() + li + float: none + display: block + background-color: none + a + display: block + padding: 4px 15px + clear: both + font-weight: normal + line-height: 18px + color: #eee + &:hover + @include vertical-gradient(#292929, #191919) + @include box-shadow(none) + // }}} // Menu {{{ .oe_menu float: left padding: 0 margin: 0 li - list-style-type: none float: left a display: block @@ -220,7 +530,6 @@ $colour4: #8a89ba li position: relative padding: 1px 0 1px 16px - list-style-type: none a display: block color: #4c4c4c @@ -238,6 +547,16 @@ $colour4: #8a89ba text-shadow: 0 1px 1px rgba(0,0,0,0.2) @include radius(4px) @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2)) + .oe_menu_counter + float: right + background: #8a89ba + color: #eee + font-size: 12px + border: 1px solid lightgray + padding: 0px 4px 0px 4px + text-shadow: 0 1px 1px rgba(0,0,0,0.2) + margin: 0px + @include radius(4px) .oe_active background: $colour4 border-top: 1px solid lightGray @@ -251,6 +570,9 @@ $colour4: #8a89ba color: $colour4 text-shadow: 0 1px 1px white @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2)) + .oe_menu_counter + background: #eee + color: #8a89ba .oe_menu_toggler:before width: 0 height: 0 @@ -273,11 +595,180 @@ $colour4: #8a89ba border-right: 4px solid transparent border-top: 4px solid #4c4c4c // }}} - - // UserMenu {{{ - .oe_dropdown + // ActionManager {{{ + .oe_application + width: 100% + a + color: $colour4 + &:hover + text-decoration: underline + // }}} + // Content Header MIT {{{ + .oe-view-manager-header + border-top: 1px solid #cacaca + border-bottom: 1px solid #cacaca + @include vertical-gradient(#fcfcfc, #dedede) + @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1))) + h2 + float: left + font-size: 18px + margin: 1px 0 + a + color: $colour4 + .buttons + padding-right: 8px + li + float: left + margin-right: 12px + .filter + vertical-align: top + li + height: 24px + line-height: 24px + padding: 0 8px + .header-row + height: 26px + line-height: 26px + margin: 8px + clear: both + text-shadow: 0 1px 1px white + .dropdown-toggle:after + margin-top: 7px + margin-left: 6px + border-top-color: #404040 + .oe_vm_switch + li + padding-left: 1px + text-align: center + width: 24px + height: 24px + line-height: 24px + .pagination + li + height: 24px + line-height: 24px + padding: 0 8px + .button-group + display: inline-block + border: 1px solid #ababab + @include radius(5px) + li + float: left + border-right: 1px solid #ababab + &:last-child + border: none + a + color: #4c4c4c + &:hover + text-decoration: none + .active + width: 100% + background: #999 + @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset) + color: #fff + text-shadow: 0 1px 2px rgba(0,0,0,0.4) + // }}} + // ViewManager.header {{{ + .oe_view_manager_header2 + width: 100% + border-top: 1px solid #cacaca + border-bottom: 1px solid #cacaca + @include vertical-gradient(#fcfcfc, #dedede) + @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1))) + h2 + float: left + font-size: 18px + margin: 1px 0 + a + color: $colour4 + td + padding: 4px 8px + line-height: 26px + .oe_button_group + display: inline-block + border: 1px solid #ababab + @include radius(5px) + li + float: left + border-right: 1px solid #ababab + &:last-child + border: none + a + color: #4c4c4c + &:hover + text-decoration: none + .active + background: #999 + @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset) + a + color: #fff + text-shadow: 0 1px 2px rgba(0,0,0,0.4) + .oe_view_manager_buttons + white-space: nowrap + // }}} + // ViewManager.body {{{ + .oe_view_manager_body + h4 + margin: 8px 0 + // }}} + // ViewManager.pager {{{ + .oe_pager_value + float: left + margin-right: 8px + .oe_pager_group + float: left + height: 24px + line-height: 24px + display: inline-block + border: 1px solid #ababab + cursor: pointer + @include radius(5px) + li + height: 24px + line-height: 24px + padding: 0 + float: left + border-right: 1px solid #ababab + &:last-child + border: none + a + color: #4c4c4c + padding: 0 8px + &:hover + text-decoration: none + .active + background: #999 + @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset) + a + color: #fff + text-shadow: 0 1px 2px rgba(0,0,0,0.4) + // }}} + // ViewManager.switches {{{ + .oe_view_manager_switch + li + padding-left: 1px + text-align: center + width: 24px + height: 24px + line-height: 24px + .oe_vm_switch_list:after, .oe_vm_switch_tree:after + content: "ö" + .oe_vm_switch_graph:after + content: "ó" + .oe_vm_switch_gantt:after + content: "y" + .oe_vm_switch_calendar:after + content: "b" + .oe_vm_switch_kanban:after + content: "ó" + .oe_vm_switch_diagram:after + content: "}" + // }}} + // ViewManager.sidebar {{{ + .oe_form_dropdown_section position: relative - + display: inline-block + margin-right: 4px .oe_dropdown_toggle:after width: 0 height: 0 @@ -285,61 +776,752 @@ $colour4: #8a89ba content: "&darr" text-indent: -99999px vertical-align: top - margin-top: 8px - margin-left: 4px border-left: 4px solid transparent border-right: 4px solid transparent border-top: 4px solid white @include opacity(0.5) - - .oe_user_menu - float: right - padding: 0 - margin: 0 + margin-top: 7px + margin-left: 6px + border-top-color: #404040 + .oe_dropdown_menu + display: none + position: absolute + top: 28px + left: 0px + padding: 8px + border: 1px solid #afafb6 + width: 120px + overflow-x: hidden + z-index: 900 + text-align: left + background: white + @include radius(3px) + @include box-shadow(0 1px 4px rgba(0,0,0,0.3)) li list-style-type: none - float: left + float: none + display: block + background-color: none + a + display: block + padding: 3px 6px + clear: both + font-weight: normal + line-height: 14px + color: #4c4c4c + text-decoration: none + &:hover + text-decoration: none + @include vertical-gradient(#f0f0fa, #eeeef6) + @include box-shadow(none) + .oe_sidebar + white-space: nowrap + // }}} + // SearchView xmo {{{ + .oe_searchview + position: relative + float: right + .VS-search + .VS-search-box + min-height: 0 + border-width: 1px + padding: 0 + width: 410px + //border: 1px solid #ababab + //margin-top: 0 + //margin-bottom: 0 + //padding: 4px 19px + //font-size: 13px + //background: url('../img/search.png') no-repeat 5px + //background-color: white + //@include radius(13px) + //@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset) + .VS-icon + top: 5px - .oe_dropdown_options - float: left - background: #333 - background: rgba(37,37,37,0.9) + .VS-icon-search + left: 5px + + .VS-search-inner + margin: 0 40px 0 17px + font-size: 13px + + input + font-size: inherit + line-height: inherit + height: auto + padding: 0 + + .VS-input-width-tester + font-size: inherit + padding: 0 + + .search_input + margin-left: 0 + height: 22px + input, .VS-input-width-tester + padding: 0 2px + height: inherit + margin: 0 + + .search_facet + padding: 0 + margin: 1px 0 + border: 1px solid $facets-border + @include radius(3px) + background: #f0f0fa + height: 18px + + &.is_selected + border-color: $facets-border-selected + @include box-shadow(0 0 3px 1px $facets-border-selected) + + .category + margin-left: 0 + .VS-icon-cancel + // don't change the icon on selection + @include opacity(1.0) + background-position: center 0 + + .category + padding: 0 4px 0 14px + color: white + background: #8786b7 + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4) + font-weight: normal + text-transform: none + height: 18px + line-height: 18px + font-size: inherit + + .search_facet_input_container + border-left: 1px solid $facets-border + height: 18px + line-height: 18px + padding: 0 4px + cursor: pointer + + .search_facet_remove + left: 1px + top: 3px + + .VS-icon-cancel + right: 24px + + .oe_vs_unfold_drawer + position: absolute + top: 0 + right: 0 + height: 100% + line-height: 23px + padding: 0 7px 0 4px + color: #ccc + cursor: pointer + &:before + content: "▾" + + &.oe_searchview_open_drawer + .oe_searchview_drawer + display: block + + .oe_searchview_drawer + position: absolute + z-index: 1 + top: 100% + right: 0 + background-color: white + width: 410px + display: none + border: 1px solid #ccc + text-align: left + + .oe_searchview_filters + display: table + width: 100% + + > div + @include unscrew-box + display: table-cell + width: 50% + + h3 + margin: 2px 4px 2px 8px + color: $section-title-color + + ul + margin: 0 12px 3px + padding: 0 + list-style: none + + li + list-style: none + padding: 3px 6px + height: 14px + color: inherit + cursor: pointer + + &:hover + background-color: #f0f0fa + + .oe_searchview_advanced + h4 + border-top: 1px solid #ccc + margin: 5px 0 3px + cursor: pointer + + h4:before + content: "▸ " + + div + display: none + + button.oe_add_condition:before + content: "⊞ " + button.oe_apply:before + content: "⚡ " + + ul + list-style: none + padding: 0 + li + list-style: none + margin: 0 + + &.oe_opened + h4:before + content: "▾ " + div + display: block + // }}} + // View.topbar {{{ + .oe_view_topbar + border-bottom: 1px solid #cacaca + @include vertical-gradient(#fcfcfc, #dedede) + padding: 0 8px + line-height: 30px + // }}} + // FormView.body {{{ + .oe_formview + background: white + .oe_form_header + padding: 8px + .oe_form_invalid + input, select, textarea + background-color: #F66 !important + border: 1px solid #D00 !important + .oe_form_button_save_dirty + color: white + background: #dc5f59 + background: -moz-linear-gradient(#dc5f59, #b33630) + background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)) + background: -webkit-linear-gradient(#dc5f59, #b33630) + -moz-box-shadow: none + -webkit-box-shadow: none + -box-shadow: none + font-weight: bold + &:hover + background: #ED6F6A + // }}} + // FormView.customdivs {{{ + .oe_form_topbar + border-bottom: 1px solid #cacaca + @include vertical-gradient(#fcfcfc, #dedede) + padding: 0 8px + line-height: 30px + button + font-size: 12px + height: 24px + line-height: 24px + vertical-align: top + padding: 0 10px + margin: 3px 0 + span.i + line-height: 20px + height: 24px + ul + height: 30px + padding: 0 + margin: 0 + text-shadow: 0 1px 1px white + border-left: 1px solid #cacaca + border-right: 1px solid #cacaca + li + padding: 0 + margin: 0 + float: left + vertical-align: top + border-right: 1px solid #cacaca + height: 30px + padding: 0 12px + &:first-child + border-left: 1px solid #cacaca + a + color: #4c4c4c + &:hover + color: black + .oe_form_steps + img + margin: 0 8px + vertical-align: top + li + border-right: none + padding: 0 + &:first-child + margin-left: 12px + border-left: none + &:last-child + margin-right: 12px + .oe_form_steps_active + font-weight: bold + color: #b33630 + .oe_application + .oe_form_sheetbg + background: url(/web/static/src/img/form_sheetbg.png) + padding: 8px + border-bottom: 1px solid #ddd + .oe_form_sheet_width + min-width: 650px + max-width: 980px + margin: 0 auto + .oe_form_sheet + padding: 8px 8px 8px 8px + background: white + min-height: 420px + border: 1px solid #afafb6 + @include box-shadow(0 0 10px rgba(0,0,0,0.3)) + //.avatar + // max-height: 52px + //.contacts + // li + // float: left + // width: 33% + // margin-bottom: 4px + // .avatar + // height: 40px + // width: 40px + // border: 1px solid + // border-color: #e5e5e5 #dbdbdb #d2d2d2 + // .data + // padding: 2px + // line-height: 18px + // }}} + // FormView.autolayout {{{ + .oe_form_autolayout + .oe_button + height: 22px + .oe_form_group_cell + line-height: 18px + padding: 2px + height: 25px + .oe_form_button + width: 100% + white-space: nowrap + .oe_form_group_cell > .oe_form_field_many2one, + .oe_form_group_cell > .oe_form_field_many2one input, + .oe_form_group_cell > .oe_form_field_char input, + .oe_form_group_cell > .oe_form_field_text textarea, + .oe_form_group_cell > .oe_form_field_email, + .oe_form_group_cell > .oe_form_field_email input, + .oe_form_group_cell > .oe_form_field_url, + .oe_form_group_cell > .oe_form_field_url input, + .oe_form_group_cell > .oe_form_field_date input, + .oe_form_group_cell > .oe_form_field_float input, + .oe_form_group_cell > .oe_form_field_selection select, + .oe_form_group_cell > .oe_datepicker_root + width: 100% + .oe_form_button.oe_button + color: #4c4c4c + white-space: nowrap + min-width: 100% + width: 100% + height: 22px + span + position: relative + vertical-align: top + .oe_form_group_cell.oe_form_group_nested + padding: 0 + // }}} + // FormView.label {{{ + .oe_form + label + font-weight: bold + .oe_form_label_help[for], .oe_form_label[for] + white-space: nowrap + // }}} + // FormView.fields {{{ + .oe_form + textarea + resize: vertical + input[type="text"], + input[type="password"], + input[type="file"], + select + height: 22px + padding-top: 2px + input[type="text"], + input[type="password"], + input[type="file"], + select, + textarea + -moz-box-sizing: border-box + -webkit-box-sizing: border-box + -ms-box-sizing: border-box + box-sizing: border-box + padding: 0 2px + margin: 0 2px + border: 1px solid #999 + -moz-border-radius: 3px + -webkit-border-radius: 3px + border-radius: 3px + background: white + min-width: 90px + color: #1f1f1f + input[readonly], + select[readonly], + textarea[readonly], + input[disabled], + select[disabled], + textarea[disabled] + background: #E5E5E5 !important + color: #666 + .oe_form_field_many2one input, + .oe_form_field_binary input, + .oe_form_field_binary input, + .oe_form_field_email input, + .oe_form_field_url input + border-right: none + -webkit-border-top-right-radius: 0px + -webkit-border-bottom-right-radius: 0px + -moz-border-radius-topright: 0px + -moz-border-radius-bottomright: 0px + border-top-right-radius: 0px + border-bottom-right-radius: 0px + .oe_form_field_email button img, + .oe_form_field_url button img + vertical-align: top + .oe_form_field_translatable, + .oe_form_field_many2one, + .oe_form_field_date, + .oe_form_field_datetime + white-space: nowrap + .oe_form_field_boolean + padding-top: 4px + .oe_form_field_datetime input + min-width: 11em + .oe_datepicker_container display: none + .oe_datepicker_root + display: inline-block + .oe_form_required + input, select, textarea + background-color: #D2D2FF !important + .oe_form_invalid + input, select, textarea + background-color: #F66 !important + border: 1px solid #D00 !important + // }}} + // FormView.buttons {{{ + .oe_form + .oe_button.oe_field_button + -webkit-border-top-left-radius: 0px + -webkit-border-bottom-left-radius: 0px + -moz-border-radius-topleft: 0px + -moz-border-radius-bottomleft: 0px + border-top-left-radius: 0px + border-bottom-left-radius: 0px + margin-right: -1px + height: 22px + .oe_input_icon + cursor: pointer + margin: 3px 0 0 -21px + vertical-align: top + .oe_input_icon_disabled position: absolute - top: 32px - right: -1px - border: 0 - z-index: 900 - margin-left: 0 - margin-right: 0 - padding: 6px 0 - zoom: 1 - border-color: #999 - border-color: rgba(0, 0, 0, 0.2) - border-style: solid - border-width: 0 1px 1px - @include radius(0 0 6px 6px) - @include box-shadow(0 1px 4px rgba(0,0,0,0.3)) - @include background-clip() + cursor: default + opacity: 0.5 + filter: alpha(opacity=50) + right: 5px + top: 3px + // }}} + // FormView.binaryfile {{{ + /* http://www.quirksmode.org/dom/inputfile.html + * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image + */ + .oe_form + .oe-binary-file-set + overflow: hidden + position: relative + width: 45px + height: 30px + input.oe-binary-file + z-index: 0 + line-height: 0 + font-size: 12px + position: absolute + // Should be adjusted for all browsers + top: 1px + right: 10px + opacity: 0 + filter: alpha(opacity = 0) + -ms-filter: "alpha(opacity=0)" + margin: 0 + padding: 0 + // }}} + // FormView.separator {{{ + .oe_horizontal_separator + font-weight: bold + border: 0 solid #666 + border-bottom-width: 1px + margin: 3px 4px 3px 1px + height: 17px + .oe_horizontal_separator:empty + height: 5px + .oe_vertical_separator + border-left: 1px solid #666 + padding: 0 4px 0 4px + // }}} + // FormView.notebook {{{ + .oe_form_notebook + margin: 8px 0 + padding: 0 8px + list-style: none + zoom: 1 + .oe_form_notebook:before, .oe_form_notebook:after + display: table + content: "" + zoom: 1 + .oe_form_notebook:after + clear: both + .oe_form_notebook > li + float: left + .oe_form_notebook > li > a + display: block + color: #4c4c4c + .oe_form_notebook + border-color: #ddd + border-style: solid + border-width: 0 0 1px + .oe_form_notebook > li + position: relative + margin-bottom: -1px + .oe_form_notebook > li > a + padding: 0 12px + margin-right: 2px + line-height: 30px + border: 1px solid transparent + @include radius(4px 4px 0 0) + .oe_form_notebook > li > a:hover + text-decoration: none + background-color: #eee + border-color: #eee #eee #ddd + .oe_form_notebook > li.ui-state-active > a, .oe_form_notebook > li.ui-state-active > a:hover + background-color: #ffffff + border: 1px solid #ddd + border-bottom-color: transparent + cursor: default + .ui-tabs-hide + display: none + + // }}} + // FormView.progressbar {{{ + .oe_form + .oe_form_field_progressbar.ui-progressbar + height: 22px + font-size: 10px + -moz-box-sizing: border-box + -webkit-box-sizing: border-box + -ms-box-sizing: border-box + box-sizing: border-box + border: 1px solid #999 + -moz-border-radius: 3px + -webkit-border-radius: 3px + border-radius: 3px + background: white + min-width: 90px + + span + position: absolute + margin-left: 10px + font-weight: bold + + .ui-widget-header + background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x + // }}} + // FormView.viewmanager {{{ + .oe_form + .oe_view_manager_header2 + td + padding: 0px 8px + line-height: 16px + .oe_i + font-size: 13px + .oe_pager_group + height: auto + line-height: 16px + li + height: auto + line-height: 16px + + // }}} + // ListView {{{ + .oe_listview_nocontent + > img + float: left + margin-right: 1.5em + > div + // don't encroach on my arrow + overflow: hidden + padding: 6px + font-size: 125% + .oe-listview-content + width: 100% + thead, tfoot + border-bottom: 1px solid #cacaca + background: #eee + tfoot + border-bottom: 1px solid #cacaca + font-weight: bold + tbody + cursor: pointer + background: white + thead + vertical-align: top + td.oe_list_checkbox:first-child, th.oe_list_checkbox:first-child + width: 17px + &:after + border-width: 0 + tbody tr:nth-child(odd) td, tbody tr:nth-child(odd) th + background-color: #f0f0fa + @include vertical-gradient(#f0f0fa, #eeeef6) + tbody tr:hover td, tbody tr:hover th + @include vertical-gradient(#eee, #dedede) + td, th + padding: 3px 6px + line-height: 18px + button + border: none + background: transparent + padding: 0 + @include box-shadow(none) + .numeric + text-align: right + width: 82px + input + text-align: right + //thead th:after + //content: "" + //float: right + //margin-top: 7px + //border-width: 0 4px 4px + //border-style: solid + //border-color: #000 transparent + //visibility: hidden + table .sortdown:after, table .sortdown:hover:after + visibility: visible + @include opacity(0.6) + table .sortup:after + border-bottom: none + border-left: 4px solid transparent + border-right: 4px solid transparent + border-top: 4px solid #000 + visibility: visible + @include box-shadow(none) + @include opacity(0.6) + // }}} + // KanbanView {{{ + .oe_kanban_group_title + margin: 1px 1px 4px + font-size: 15px + font-weight: bold + text-shadow: 0 1px 0 white + .oe_kanban_column, .oe_kanban_group_header + width: 240px + vertical-align: top + padding: 6px 7px 6px 6px + background: #f0eeee + border-left: 1px solid #f0f8f8 + border-right: 1px solid #b9b9b9 + .oe_kanban_record + position: relative + display: block + min-height: 50px + margin: 6px 0 + display: block + @include radius(4px) + &:last-child + margin-bottom: 0 + .oe_kanban_title + font-weight: bold + margin: 2px 4px + .oe_kanban_gravatar + width: 20px + height: 20px + @include radius(3px) + @include box-shadow(0 1px 2px rgba(0,0,0,0.2)) + .oe_kanban_avatar_toto + height: 40px + width: 40px + border: 1px solid + border-color: #e5e5e5 #dbdbdb #d2d2d2 + @include radius(3px) + @include box-shadow(0 1px 2px rgba(0,0,0,0.2)) + // }}} + // Translation {{{ + .oe_trad_field.touched + border: 1px solid green !important + // }}} + // Tooltips {{{ + &.oe_tooltip + font-size: 12px + .oe_tooltip_string + color: #FD5 + font-weight: bold + font-size: 13px + .oe_tooltip_help + white-space: pre-wrap + .oe_tooltip_technical + padding: 0 0 4px 0 + margin: 5px 0 0 15px li - float: none - display: block - background-color: none - a - display: block - padding: 4px 15px - clear: both - font-weight: normal - line-height: 18px - color: #eee - &:hover - @include vertical-gradient(#292929, #191919) - @include box-shadow(none) + list-style: circle + .oe_tooltip_technical_title + font-weight: bold // }}} + // Debugging stuff {{{ + .oe_layout_debugging + .oe_form_group + outline: 2px dashed red + .oe_form_group_cell + outline: 1px solid blue -.openerp - // Transitional overrides for old styles {{{ + .oe_debug_view + float: left + .oe_debug_view_log + font-size: 95% + .oe_debug_view_log label + display: block + width: 49% + text-align: right + float: left + font-weight: bold + color: #009 + .oe_debug_view_log span + display: block + width: 49% + float: right + color: #333 // }}} +// Transitional overrides for old styles {{{ +.openerp + .oe_form_field_many2one + td:first-child + position: relative + span.oe-m2o-drop-down-button + position: absolute + top: 3px + right: 2px +// }}} + // au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css" // vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker: diff --git a/addons/web/static/src/css/base_old.css b/addons/web/static/src/css/base_old.css index 2b6f6fbf639..8f333fae761 100644 --- a/addons/web/static/src/css/base_old.css +++ b/addons/web/static/src/css/base_old.css @@ -1,7 +1,6 @@ .openerp { padding: 0; margin: 0; - height: 100%; font-size: 80%; font-family: Ubuntu, Helvetica, sans-serif; } @@ -14,10 +13,9 @@ .openerp .view-manager-main-content { width: 100%; - padding: 0 8px 8px 8px; } -.openerp .oe_form_frame_cell .view-manager-main-content { +.openerp .oe_form_group_cell .view-manager-main-content { padding: 0; } @@ -56,216 +54,6 @@ display: none; } -/* Loading */ -.loading { - cursor: wait; -} -.openerp .loading { - display: none; - z-index: 100; - position: fixed; - top: 0; - right: 50%; - padding: 4px 12px; - background: #A61300; - color: white; - text-align: center; - border: 1px solid #900; - border-top: none; - -moz-border-radius-bottomright: 8px; - -moz-border-radius-bottomleft: 8px; - border-bottom-right-radius: 8px; - border-bottom-left-radius: 8px; -} -.openerp .oe_notification { - z-index: 1050; - display: none; -} -.openerp .oe_notification * { - color: white; -} - -/* Login page */ - -.login { - padding: 0; - margin: 0; - font-family: "Lucida Grande", Helvetica, Verdana, Arial; - background: url("/web/static/src/img/pattern.png") repeat; - color: #eee; - font-size: 14px; - height: 100%; -} - -.login ul, ol { - padding: 0; - margin: 0; -} - -.login li { - list-style-type: none; - padding-bottom: 4px; -} - -.login a { - color: #eee; - text-decoration: none; -} - -.login button { - float: right; - display: inline-block; - cursor: pointer; - padding: 6px 16px; - font-size: 13px; - font-family: "Lucida Grande", Helvetica, Verdana, Arial; - border: 1px solid #222222; - color: white; - margin: 0; - background: #600606; - background: -moz-linear-gradient(#b92020, #600606); - background: -webkit-gradient(linear, left top, left bottom, from(#b92020), to(#600606)); - background: -ms-linear-gradient(top, #b92020, #600606); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b92020', endColorstr='#600606',GradientType=0 ); - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(155, 155, 155, 0.4) inset; - -o-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; -} - -.login input, .login select { - width: 252px; - font-size: 14px; - font-family: "Lucida Grande", Helvetica, Verdana, Arial; - border: 1px solid #999999; - background: whitesmoke; - -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); - -box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; -} - -.login input { - margin-bottom: 9px; - padding: 5px 6px; -} - -.login select { - padding: 1px; -} - -.login .dbpane { - position: fixed; - top: 0; - right: 8px; - padding: 5px 10px; - color: #eee; - border: solid 1px #333; - background: #1e1e1e; - background: rgba(30,30,30,0.94); - -moz-border-radius: 0 0 8px 8px; - -webkit-border-radius: 0 0 8px 8px; - border-radius: 0 0 8px 8px; -} - -.login .bottom { - position: absolute; - top: 50%; - left: 0; - right: 0; - bottom: 0; - text-shadow: 0 1px 1px #999999; - background: #600606; - background: -moz-linear-gradient(#b41616, #600606); - background: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606)); - background: -ms-linear-gradient(top, #b41616, #600606); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b41616', endColorstr='#600606',GradientType=0 ); -} - -.login .pane { - position: absolute; - top: 50%; - left: 50%; - margin: -160px -166px; - border: solid 1px #333333; - background: #1e1e1e; - background: rgba(30,30,30,0.94); - padding: 22px 32px; - text-align: left; - -moz-border-radius: 8px; - -webkit-border-radius: 8px; - border-radius: 8px; - -moz-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); - -webkit-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); - -box-shadow: 0 0 18px rgba(0, 0, 0, 0.9); -} - -.login .pane h2 { - margin-top: 0; - font-size: 18px; -} - -.login #logo { - position: absolute; - top: -70px; - left: 0; - width: 100%; - margin: 0 auto; - text-align: center; -} - -.login .footer { - position: absolute; - bottom: -40px; - left: 0; - width: 100%; - text-align: center; -} - -.login .footer a { - font-size: 13px; - margin: 0 8px; -} - -.login .footer a:hover { - text-decoration: underline; -} - -.login .openerp { - font-weight: bold; - font-family: serif; - font-size: 16px; -} - -.openerp .login { - text-align: center; -} - -.openerp .login .login_error_message { - display: none; - background-color: #b41616; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); - -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); - color: #eee; - font-size: 14px; - padding: 14px 18px; - margin-top: 15px; - text-align: center; -} - -.openerp .login.login_invalid .login_error_message { - display: inline-block; -} - /* Database */ @@ -404,201 +192,6 @@ label.error { padding-bottom:4px; } -/* View Manager */ -.openerp .oe_vm_switch { - float: right; -} -.openerp .oe-view-manager-header .oe_view_title { - font-size:150%; - padding:2px 0 0 0; -} - -/* SearchView */ -.openerp .oe_searchview_field > div { - position: relative; - white-space: nowrap; -} -.openerp .oe_searchview_field .oe_input_icon { - top: auto; - bottom: 3px; -} - -.openerp .filter_label, .openerp .filter_icon { - background: #F0F0F0; - border: 1px solid #999; - background: -moz-linear-gradient(top, #F0F0F0 0%, #C0C0C0 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F0F0F0), color-stop(100%,#C0C0C0)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#C0C0C0',GradientType=0 ); -} -.openerp .filter_label:hover, .openerp .filter_icon:hover { - background: #F0F0F0; - background: -moz-linear-gradient(top, #F0F0F0 0%, #A1A7CE 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F0F0F0), color-stop(100%,#A1A7CE)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#A1A7CE',GradientType=0 ); -} -.openerp .filter_label:active, .openerp .filter_icon:active { - background: #aaa; - background: -moz-linear-gradient(top, #999999 0%, #EEEEEE 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#999999), color-stop(100%,#EEEEEE)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#999999', endColorstr='#EEEEEE',GradientType=0 ); -} -.openerp .filter_label.enabled, .openerp .filter_icon.enabled { - background: #aaa; - filter: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - -o-box-shadow: none; - box-shadow: none; -} -.openerp .filter_icon { - height: 22px; - padding: 1px 2px 0 2px; - margin: 0; - vertical-align: bottom; -} -.openerp .filter_label { - font-weight: bold; - text-transform: uppercase; - text-shadow: #EEE 0 1px 0; - color: #4C4C4C; - white-space: nowrap; - min-height: 40px; - min-width: 75px; - padding: 2px 4px; - margin: 0; -} -.openerp .filter_label_group { - padding-right: 0.4em; - white-space: nowrap; -} - -.openerp .filter_label_group button { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - border-right: none; -} -.openerp .filter_label_group button:first-child { - -webkit-border-top-left-radius: 7px; - -webkit-border-bottom-left-radius: 7px; - -moz-border-radius-topleft: 7px; - -moz-border-radius-bottomleft: 7px; - border-top-left-radius: 7px; - border-bottom-left-radius: 7px; - border-right: none; -} -.openerp .filter_label_group button:last-child { - -webkit-border-top-right-radius: 7px; - -webkit-border-bottom-right-radius: 7px; - -moz-border-radius-topright: 7px; - -moz-border-radius-bottomright: 7px; - border-top-right-radius: 7px; - border-bottom-right-radius: 7px; - border-right: 1px solid #999; -} -.openerp .filter_label_group button.filter_icon img { - padding: 1px 8px 0 8px; -} -.openerp .filter_label_group button.filter_icon:first-child { - border-left: solid 1px #999; - margin-left: -7px; - -webkit-border-top-left-radius: 0; - -webkit-border-bottom-left-radius: 0; - -moz-border-radius-topleft: 0; - -moz-border-radius-bottomleft: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.openerp .searchview_group_string { - display: block; - color: #7D7979; - font-weight: bold; - padding: 2px 0 2px 10px; - text-decoration: none; -} -.openerp .searchview_group_string:hover { - background-color: #ccc; -} -.openerp .searchview_group.folded .searchview_group_string { - background: url("/web/static/src/img/ui/group-folded.png") no-repeat scroll 0 50%; -} -.openerp .searchview_group.folded .searchview_group_content { - display: none; -} -.openerp .searchview_group.expanded .searchview_group_string { - background: url("/web/static/src/img/ui/group-expanded.png") no-repeat scroll 0 50%; -} -.openerp .searchview_group.expanded .searchview_group_content { - display: block; - padding-bottom:3px; -} - -.openerp .searchview_group_content .oe_label, .openerp .searchview_group_content .oe_label_help { - font-weight: bold; - color: #4c4c4c; -} - -.openerp .oe-searchview-render-line .oe_label, .openerp .oe-searchview-render-line .oe_label_help { - font-weight: bold; - font-size: 80%; - white-space: nowrap; -} - -.openerp .searchview_extended_group { - padding: 3px; - margin: 2px; -} - -.openerp .searchview_extended_group .oe_adv_filters_and { - border-bottom: 1px solid #8E8E8E; - text-align: center; - margin-top: -10px; -} -.openerp .searchview_extended_group .oe_adv_filters_and span { - background: #F0EEEE; - position: relative; - top: 0.5em; - padding: 0 1em 0 1em; - color: #8E8E8E; -} - -.openerp .searchview_extended_group.last_group .oe_adv_filters_and { - display: none; -} - -.openerp .oe_search-view-buttons { - padding: 2px 0 10px 0; - vertical-align:middle; -} -.openerp .oe_search-view-filters-management { - float: right; -} -.openerp .oe_search-view-filters-management, .openerp .oe_search-view-custom-filter-btn { - float:right; -} - -.openerp .searchview_extended_add_proposition span { - font-size: 0.9em; - background: url(/web/static/src/img/icons/gtk-add.png) repeat-y; - padding-left: 18px; -} - -.openerp .searchview_extended_delete_group { - float:right; - display: none; -} - -.openerp .searchview_extended_delete_prop { - text-decoration: none; -} - -.openerp .searchview_extended_delete_group span, -.openerp .searchview_extended_delete_prop span { - font-size: 0.9em; - background: url(/web/static/src/img/icons/gtk-close.png) repeat-y; - padding-left: 18px; -} /* List */ .openerp table.oe-listview-content { clear: right; @@ -818,91 +411,19 @@ label.error { border-bottom-right-radius: 4px; } -/* Notebook */ -.openerp .oe_form_notebook { - padding: 0; - background: none; - border-width: 0; -} -.openerp .oe_form_notebook .ui-tabs-panel { - padding: 4px; - -moz-border-radius-topright: 4px; - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; -} -.openerp .oe_form_notebook ul.ui-tabs-nav { - padding-left: 0; - background: transparent; - border-width: 0; - border-radius: 0; - -moz-border-radius: 0; - -webkit-border-radius: 0; - line-height: 0.8em; - font-size: 95%; - color: #555; -} -.openerp .oe_form_notebook ul.ui-tabs-nav li { - font-weight: bold; -} -.openerp .oe_form_notebook .ui-tabs-panel { - background: #f9f9f9; - border-width: 1px; -} -.openerp .oe_form_notebook .ui-tabs-selected { - background: #f9f9f9; -} -/* Unedit Form */ -.openerp .field_char, -.openerp .field_date, -.openerp .field_float, -.openerp .field_selection, -.openerp a.oe_form_uri { - vertical-align: middle; - padding-top: 3px; - font-size: 90%; - color: #222; -} -.openerp a.oe_form_uri { - color: #9A0404; - line-height: 12px; -} - - - /* Form */ -.openerp .oe_form_button_save_dirty { - display: none; -} -.openerp .oe_form_dirty > .oe_form_header > .oe_form_buttons > .oe_form_button_save { - color: white; - background: #dc5f59; - background: -moz-linear-gradient(#dc5f59, #b33630); - background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)); - background: -webkit-linear-gradient(#dc5f59, #b33630); - -moz-box-shadow: none; - -webkit-box-shadow: none; - -box-shadow: none; - font-weight: bold; -} -.openerp .oe_form_frame_cell input[type="checkbox"] { +.openerp .oe_form_group_cell input[type="checkbox"] { margin-top: 3px; vertical-align: middle; } -.openerp .oe_form_frame_cell .input[type="text"] { +.openerp .oe_form_group_cell .input[type="text"] { padding-bottom: 1px; } -.openerp table.oe_frame td { +.openerp table.oe_form_group td { color: #4c4c4c; } -.openerp td.oe_form_frame_cell { - padding: 2px; - position: relative; -} -.openerp .oe_frame.oe_forms { - clear: both; -} -.openerp table.oe_frame { +.openerp table.oe_form_group { color: #4c4c4c; } .openerp fieldset.oe_group_box { @@ -916,22 +437,6 @@ label.error { .openerp fieldset.oe_group_box legend { font-weight: bold; } -.openerp td.oe_form_frame_cell { - padding: 2px; - position: relative; -} -.openerp td.oe_form_field_translatable, -.openerp td.oe_form_field_many2one, -.openerp td.oe_form_field_date, -.openerp td.oe_form_field_datetime { - white-space: nowrap; -} -.openerp td.oe_form_field_boolean { - padding-top: 4px; -} -.openerp td.oe_form_frame_cell.oe_form_group { - padding: 0; -} .openerp .required.error { border: 1px solid #900; } @@ -949,66 +454,6 @@ label.error { margin-right: 3px; } - -.openerp label.oe_label_help, .openerp label.oe_label, -.openerp .oe_form_paragraph, -.openerp .oe_form_field_statusbar, -.openerp .oe_forms input[type="text"], -.openerp .oe_forms input[type="password"], -.openerp .oe_forms input[type="file"], -.openerp .oe_forms select, -.openerp .oe_forms .oe_button, -.openerp .oe_forms textarea { - font-size: 85%; -} - -.openerp label.oe_label_help, .openerp label.oe_label { - display: block; - color: #4c4c4c; - font-weight: normal; -} -.openerp label.oe_label_help { - cursor: help; -} -.openerp .oe_form_frame_cell .oe_label, .openerp .oe_form_frame_cell .oe_label_help { - font-weight: normal; -} -.openerp #tiptip_content { - font-size: 12px; -} -.openerp .oe_tooltip_string { - color: #FD5; - font-weight: bold; - font-size: 13px; -} -.openerp .oe_tooltip_help { - white-space: pre-wrap; -} -.openerp .oe_tooltip_technical { - padding: 0 0 4px 0; - margin: 5px 0 0 15px; - list-style: circle; -} -.openerp .oe_tooltip_technical_title { - font-weight: bold; -} - -.openerp .oe_forms label.oe_label, .openerp .oe_forms label.oe_label_help { - margin: 3px 0 0 3px; - white-space: nowrap; -} -.openerp .oe_forms .searchview_group_content label.oe_label, .openerp .searchview_group_content .oe_forms label.oe_label_help { /* making a distinction between labels in search view and other labels */ - margin: 3px 0 0 3px; -} - -.openerp label.oe_label_help span { - font-size: 80%; - color: darkgreen; - vertical-align:top; - position: relative; - top: -4px; - padding: 0 2px; -} .openerp .oe_align_left { text-align: left; } @@ -1018,12 +463,12 @@ label.error { .openerp .oe_align_center { text-align: center; } -.openerp .oe_forms .oe_form_paragraph { +.openerp .oe_form .oe_form_paragraph { margin: 3px 0 0 0; white-space: normal; } -.openerp .oe_forms .oe_form_paragraph.oe_multilines { +.openerp .oe_form .oe_form_paragraph.oe_multilines { white-space: pre; } @@ -1033,13 +478,13 @@ label.error { margin: 0 0.5em 0 0; } -.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon, -.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon { +.openerp .oe_form .oe-listview th.oe-sortable .ui-icon, +.openerp .oe_form .oe-listview th.oe-sortable .ui-icon { height: 100%; margin-top: -9px; } -.openerp table.oe_frame .oe-listview-content td { +.openerp table.oe_form_group .oe-listview-content td { color: inherit; } @@ -1047,19 +492,19 @@ label.error { .openerp .oe_form_readonly { } -.openerp .oe_form_readonly .oe_form_frame_cell .field_text, +.openerp .oe_form_readonly .oe_form_group_cell .field_text, .openerp .oe_form_readonly .field_char, .openerp .oe_form_readonly .field_int, .openerp .oe_form_readonly .field_float, .openerp .oe_form_readonly .field_email, .openerp .oe_form_readonly .field_date, .openerp .oe_form_readonly .field_selection, -.openerp .oe_forms_readonly .oe_form_field_many2one { +.openerp .oe_form_readonly .oe_form_field_many2one { padding: 3px 2px 2px 2px; background-color: white; height: 17px; } -.openerp .oe_form_readonly .oe_form_frame_cell .field_text { +.openerp .oe_form_readonly .oe_form_group_cell .field_text { height: auto; } .openerp .oe_form_readonly .field_datetime { @@ -1084,211 +529,34 @@ label.error { .openerp .oe_form_readonly .oe_form_field_text_html div.field_text_html { white-space: pre-wrap; } -.openerp .oe_form_readonly .oe_form_frame_cell .field_text { +.openerp .oe_form_readonly .oe_form_group_cell .field_text { min-height:100px; } /* Inputs */ -.openerp .oe_forms input[type="text"], -.openerp .oe_forms input[type="password"], -.openerp .oe_forms input[type="file"], -.openerp .oe_forms select, -.openerp .oe_forms textarea { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - padding: 0 2px; - margin: 0 2px; - border: 1px solid #999; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - background: white; - min-width: 90px; - color: #1f1f1f; -} - -.openerp .oe_forms input.field_many2one, -.openerp .oe_forms input.field_binary, -.openerp .oe_forms input.field_binary, -.openerp .oe_forms input.field_email, -.openerp .oe_forms input.field_url { - border-right: none; - -webkit-border-top-right-radius: 0px; - -webkit-border-bottom-right-radius: 0px; - -moz-border-radius-topright: 0px; - -moz-border-radius-bottomright: 0px; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} -.openerp .oe_button.oe_field_button { - -webkit-border-top-left-radius: 0px; - -webkit-border-bottom-left-radius: 0px; - -moz-border-radius-topleft: 0px; - -moz-border-radius-bottomleft: 0px; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - margin-right:-1px; - height: 22px; -} - -.openerp .oe_form_field_email button img, -.openerp .oe_form_field_url button img { - vertical-align: top; -} /* vertically recentering filter management select tag */ .openerp select.oe_search-view-filters-management { margin-top:2px; } -.openerp .oe_forms select{ - padding-top: 2px; -} -.openerp .oe_forms input[readonly], -.openerp .oe_forms select[readonly], -.openerp .oe_forms textarea[readonly], -.openerp .oe_forms input[disabled], -.openerp .oe_forms select[disabled], -.openerp .oe_forms textarea[disabled]{ - background: #E5E5E5 !important; - color: #666; -} -.openerp .oe_forms textarea { - resize:vertical; -} -.openerp .oe_forms input[type="text"], -.openerp .oe_forms input[type="password"], -.openerp .oe_forms input[type="file"], -.openerp .oe_forms select, -.openerp .oe_forms .oe_button { - height: 22px; -} -.openerp .oe_forms input.field_datetime { - min-width: 11em; -} -.openerp .oe_forms .oe_form_button .oe_button { - color: #4c4c4c; - white-space: nowrap; - min-width: 100%; - width: 100%; -} @-moz-document url-prefix() { /* Strange firefox behaviour on width: 100% + white-space: nowrap */ - .openerp .oe_forms .oe_form_button .oe_button { + .openerp .oe_form .oe_form_button .oe_button { width: auto; } } /* IE Hack - for IE < 9 * Avoids buttons overflow * */ -.openerp .oe_forms .oe_form_button .oe_button { +.openerp .oe_form .oe_form_button .oe_button { min-width: auto\9; } -.openerp .oe_forms .button { - height: 22px; -} -.openerp .oe_forms .oe_button span { - position: relative; - vertical-align: top; -} -.openerp .oe_input_icon { - cursor: pointer; - margin: 3px 0 0 -21px; - vertical-align: top; -} -.openerp .oe_datepicker_container { - display: none; -} -.openerp .oe_datepicker_root { - display: inline-block; -} -.openerp .oe_form_frame_cell .oe_datepicker_root { - width: 100%; -} -.openerp .oe_input_icon_disabled { - position: absolute; - cursor: default; - opacity: 0.5; - filter:alpha(opacity=50); - right: 5px; - top: 3px; -} -.openerp .oe_trad_field.touched { - border: 1px solid green !important; -} - -/* http://www.quirksmode.org/dom/inputfile.html - * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image - */ -.openerp .oe-binary-file-set { - overflow: hidden; - position: relative; -} -.openerp input.oe-binary-file { - z-index: 0; - line-height: 0; - font-size: 12px; - position: absolute; - /* Should be adjusted for all browsers */ - top: 1px; - right: 10px; - opacity: 0; - filter: alpha(opacity = 0); - -ms-filter: "alpha(opacity=0)"; - margin: 0; - padding:0; -} -/* Widgets */ -.openerp .separator { - border: 0 solid #666; -} -.openerp .separator.horizontal { - font-weight: bold; - border-bottom-width: 1px; - margin: 3px 4px 3px 1px; - height: 17px; - font-size: 95%; -} -.openerp .separator.horizontal:empty { - height: 5px; -} -.openerp .oe_form_frame_cell.oe_form_separator_vertical { - border-left: 1px solid #666; -} -.openerp td.required input, .openerp td.required select, .openerp td.required textarea { - background-color: #D2D2FF !important; -} -.openerp td.invalid input, .openerp td.invalid select, .openerp td.invalid textarea { - background-color: #F66 !important; - border: 1px solid #D00 !important; -} -.openerp div.oe-progressbar span { - position: absolute; - margin-left: 10px; - margin-top: 5px; - font-weight: bold; -} /* jQuery UI override */ .openerp .ui-widget { font-size: 1em; } -.openerp .oe_form_field_progressbar .ui-progressbar { - height: 22px; - font-size: 10px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - border: 1px solid #999; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - background: white; - min-width: 90px; -} .openerp tbody.ui-widget-content { margin-bottom: 10px; border-spacing: 4px; @@ -1296,10 +564,6 @@ label.error { .openerp .ui-widget-header { background: white none; } -/* progress bars */ -.openerp .ui-progressbar .ui-widget-header { - background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; -} /* Sidebar */ .openerp .view-manager-main-table { @@ -1318,7 +582,7 @@ label.error { background: url("/web/static/src/img/sep-a.gif") 0 100% repeat-x; margin:6px 0 6px 2px; } -.openerp .oe_form_frame_cell .oe-view-manager-header { /* Trick: remove the background when element is in a formular */ +.openerp .oe_form_group_cell .oe-view-manager-header { /* Trick: remove the background when element is in a formular */ background: none; } @@ -1354,45 +618,6 @@ label.error { text-decoration: underline; color: inherit; } -.openerp .oe-view-manager-logs { - clear: both; - background: #fff; - margin: 0.25em 0; - font-size: 85%; - color: #4C4C4C; - position: relative; - overflow: hidden; -} -.openerp .oe-view-manager-logs ul { - margin: 0; - padding: 0 10px; - list-style: none; -} -.openerp .oe-view-manager-logs li:before { - content: '\2192 '; -} -.openerp .oe-view-manager-logs a { - text-decoration: none; - color: inherit; -} -/* only display first three log items of a folded logs list */ -.openerp .oe-view-manager-logs.oe-folded li:nth-child(n+4) { - display: none; -} -/* display link to more logs if there are more logs to view and the logview is - currently folded */ -.openerp .oe-view-manager-logs a.oe-more-logs { - display: none; -} -.openerp .oe-view-manager-logs.oe-folded.oe-has-more a.oe-more-logs { - display: block; -} -.openerp .oe-view-manager-logs a.oe-remove-everything { - position: absolute; - top: 0; - right: 0; - cursor: pointer; -} .openerp .view-manager-main-sidebar { width: 180px; @@ -1810,228 +1035,6 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { margin: 0 20px; } -.openerp .oe_vm_switch { - margin:2px 0 0 0; -} - -.openerp .oe_vm_switch_form, -.openerp .oe_vm_switch_page, -.openerp .oe_vm_switch_tree, -.openerp .oe_vm_switch_list, -.openerp .oe_vm_switch_graph, -.openerp .oe_vm_switch_gantt, -.openerp .oe_vm_switch_calendar, -.openerp .oe_vm_switch_kanban, -.openerp .oe_vm_switch_diagram { - background: url("/web/static/src/img/views-icons-a.png") repeat-x scroll left top transparent; - overflow: hidden; - width: 22px; - height: 21px; - border: none; - background-position: 0px 0px; -} - -.openerp .oe_vm_switch_form span, -.openerp .oe_vm_switch_page span, -.openerp .oe_vm_switch_tree span, -.openerp .oe_vm_switch_list span, -.openerp .oe_vm_switch_graph span, -.openerp .oe_vm_switch_gantt span, -.openerp .oe_vm_switch_calendar span, -.openerp .oe_vm_switch_kanban span, -.openerp .oe_vm_switch_diagram span { - display: none; -} - -.openerp .oe_vm_switch_list { - background-position: 0px 0px; -} -.openerp .oe_vm_switch_list:active, -.openerp .oe_vm_switch_list:hover, -.openerp .oe_vm_switch_list:focus, -.openerp .oe_vm_switch_list[disabled="disabled"] { - background-position: 0px -21px; -} - -.openerp .oe_vm_switch_tree { - background-position: 0px 0px; -} -.openerp .oe_vm_switch_tree:active, -.openerp .oe_vm_switch_tree:hover, -.openerp .oe_vm_switch_tree:focus, -.openerp .oe_vm_switch_tree[disabled="disabled"] { - background-position: 0px -21px; -} - -.openerp .oe_vm_switch_form { - background-position: -22px 0px; -} -.openerp .oe_vm_switch_form:active, -.openerp .oe_vm_switch_form:hover, -.openerp .oe_vm_switch_form:focus, -.openerp .oe_vm_switch_form[disabled="disabled"] { - background-position: -22px -21px; -} - -.openerp .oe_vm_switch_page { - background-position: -22px 0px; -} -.openerp .oe_vm_switch_page:active, -.openerp .oe_vm_switch_page:hover, -.openerp .oe_vm_switch_page:focus, -.openerp .oe_vm_switch_page[disabled="disabled"] { - background-position: -22px -21px; -} -.openerp .oe_vm_switch_graph { - background-position: -44px 0px; -} -.openerp .oe_vm_switch_graph:active, -.openerp .oe_vm_switch_graph:hover, -.openerp .oe_vm_switch_graph:focus, -.openerp .oe_vm_switch_graph[disabled="disabled"] { - background-position: -44px -21px; -} - -.openerp .oe_vm_switch_gantt { - background-position: -66px 0px; -} -.openerp .oe_vm_switch_gantt:active, -.openerp .oe_vm_switch_gantt:hover, -.openerp .oe_vm_switch_gantt:focus, -.openerp .oe_vm_switch_gantt[disabled="disabled"] { - background-position: -66px -21px; -} - -.openerp .oe_vm_switch_calendar { - background-position: -88px 0px; -} -.openerp .oe_vm_switch_calendar:active, -.openerp .oe_vm_switch_calendar:hover, -.openerp .oe_vm_switch_calendar:focus, -.openerp .oe_vm_switch_calendar[disabled="disabled"] { - background-position: -88px -21px; -} -.openerp .oe_vm_switch_kanban { - background-position: -110px 0px; -} -.openerp .oe_vm_switch_kanban:active, -.openerp .oe_vm_switch_kanban:hover, -.openerp .oe_vm_switch_kanban:focus, -.openerp .oe_vm_switch_kanban[disabled="disabled"] { - background-position: -110px -21px; -} - -.openerp .oe_vm_switch_diagram { - background-position: 0px 0px; -} -.openerp .oe_vm_switch_diagram:active, -.openerp .oe_vm_switch_diagram:hover, -.openerp .oe_vm_switch_diagram:focus, -.openerp .oe_vm_switch_diagram[disabled="disabled"] { - background-position: 0px -21px; -} - -/* Buttons */ -.openerp .oe_button:link, -.openerp .oe_button:visited, -.openerp .oe_button { - display: inline-block; - border: 1px solid #ababab; - color: #404040; - font-size: 12px; - padding: 3px 10px; - text-align: center; - -o-background-size: 100% 100%; - -moz-background-size: 100% 100%; - -webkit-background-size: auto auto !important; - background-size: 100% 100%; - background: #d8d8d8 none; - background: none, -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8)); - background: none, -webkit-linear-gradient(#efefef, #d8d8d8); - background: none, -moz-linear-gradient(#efefef, #d8d8d8); - background: none, -o-linear-gradient(top, #efefef, #d8d8d8); - background: none, -khtml-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8)); - background: -ms-linear-gradient(top, #efefef, #d8d8d8); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#efefef', endColorstr='#d8d8d8',GradientType=0 ); - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - -o-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; - -o-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); - -webkit-font-smoothing: antialiased; - outline: none; -} - -.openerp .oe_button:hover { - -o-background-size: 100% 100%; - -moz-background-size: 100% 100%; - -webkit-background-size: auto auto !important; - background-size: 100% 100%; - background: #e3e3e3 none; - background: none, -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); - background: none, -webkit-linear-gradient(#f6f6f6, #e3e3e3); - background: none, -moz-linear-gradient(#f6f6f6, #e3e3e3); - background: none, -o-linear-gradient(top, #f6f6f6, #e3e3e3); - background: none, -khtml-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); - background: -ms-linear-gradient(top, #f6f6f6, #e3e3e3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#e3e3e3',GradientType=0 ); - cursor: pointer; -} - -.openerp .oe_button:focus { - border: 1px solid #80bfff; - -o-background-size: 100% 100%; - -moz-background-size: 100% 100%; - -webkit-background-size: auto auto !important; - background-size: 100% 100%; - background: #e3e3e3, none; - background: none, -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); - background: none, -webkit-linear-gradient(#f6f6f6, #e3e3e3); - background: none, -moz-linear-gradient(#f6f6f6, #e3e3e3); - background: none, -o-linear-gradient(top, #f6f6f6, #e3e3e3); - background: none, -khtml-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); - background: -ms-linear-gradient(top, #f6f6f6, #e3e3e3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#e3e3e3',GradientType=0 ); - -moz-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; - -webkit-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; - -o-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; - box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset; -} - -.openerp .oe_button:active, -.openerp .oe_button.active { - background: #e3e3e3; - background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f; - background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f; - background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f; - background: -ms-linear-gradient(top, #e3e3e3, #f6f6f6); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e3e3e3', endColorstr='#f6f6f6',GradientType=0 ); - -moz-box-shadow: none, 0 0 0 transparent; - -webkit-box-shadow: none, 0 0 0 transparent; - -o-box-shadow: none, 0 0 0 transparent; - box-shadow: none, 0 0 0 transparent; -} - -.openerp .oe_button.disabled, -.openerp .oe_button:disabled { - background: #efefef !important; - border: 1px solid #d1d1d1 !important; - font-size: 12px; - padding: 3px 10px; - -moz-box-shadow: none !important, 0 0 0 transparent; - -webkit-box-shadow: none !important, 0 0 0 transparent; - -o-box-shadow: none !important, 0 0 0 transparent; - box-shadow: none !important, 0 0 0 transparent; - color: #aaaaaa !important; - cursor: default; - text-shadow: 0 1px 1px white !important; -} - .openerp select.oe_search-view-filters-management { font-style: oblique; color: #999999; @@ -2043,24 +1046,6 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { color: black; } -/* Debug stuff */ -.openerp .oe_debug_view_log { - font-size: 95%; -} -.openerp .oe_debug_view_log label { - display: block; - width: 49%; - text-align: right; - float: left; - font-weight: bold; - color: #009; -} -.openerp .oe_debug_view_log span { - display: block; - width: 49%; - float: right; - color: #333; -} /* Internet Explorer Fix */ a img { diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.eot b/addons/web/static/src/font/mnmliconsv21-webfont.eot Binary files differnew file mode 100755 index 00000000000..433ebb82e4b --- /dev/null +++ b/addons/web/static/src/font/mnmliconsv21-webfont.eot diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.svg b/addons/web/static/src/font/mnmliconsv21-webfont.svg new file mode 100755 index 00000000000..34b20ff992f --- /dev/null +++ b/addons/web/static/src/font/mnmliconsv21-webfont.svg @@ -0,0 +1,149 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : MIT License +Designer : Carlos Elas +Foundry : Carlos Elias +Foundry URL : httpcarloseliascl +</metadata> +<defs> +<font id="webfontfL60xSgA" horiz-adv-x="1536" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="512" /> +<glyph horiz-adv-x="0" /> +<glyph horiz-adv-x="0" /> +<glyph unicode="
" horiz-adv-x="512" /> +<glyph unicode=" " horiz-adv-x="512" /> +<glyph unicode="	" horiz-adv-x="512" /> +<glyph unicode=" " horiz-adv-x="512" /> +<glyph unicode="!" horiz-adv-x="1556" d="M1302 840q30 -10 52 -31t35 -47t16 -55t-7 -59q-12 -34 -36 -58t-54 -38q-26 -52 -52 -114t-38 -96q-10 -32 -23 -80l-27 -98q32 -38 32 -92q0 -64 -44 -108t-108 -44q-34 0 -65 15t-51 41q-30 2 -62 6t-63 6t-59 4t-46 2q-36 0 -97 -5t-115 -9q-28 -38 -72 -52 q-60 -20 -116 8t-76 86q-14 40 -6 78t32 68l-23 90q-15 60 -31 112q-12 34 -34 86l-44 100q-40 12 -74 50q-40 50 -33 112t55 102q66 52 146 26l33 21q21 13 44 30l47 34q24 17 44 35q34 28 86 76l94 88q4 50 32 84q40 48 102 54t112 -34q28 -22 41 -51t15 -63l91 -89 q49 -47 77 -71q28 -22 77 -57l95 -65q46 18 98 2zM1160 814h-2h2zM1148 804l-4 -4zM1138 792l-4 -4zM1128 780q-4 -4 -4 -6q0 2 4 6zM1118 764q0 -2 -1 -2t-1 -2q0 2 1 2t1 2zM156 552h2h-2zM138 552h2h-2zM214 558h-6h6zM174 552h-2h2zM226 562l4 4zM242 570q0 2 2 4 q-2 -2 -2 -4zM260 584l12 12zM522 64v4v-4zM500 -6v2v-2zM506 10q2 2 4 2q-2 0 -4 -2zM514 26v2v-2zM518 104v-6v6zM520 86q2 -2 2 -6q0 4 -2 6zM920 152q36 52 90 66q22 48 41 97t31 83q18 52 32 112t22 90q-16 20 -25 44t-9 52l-88 84q-48 44 -82 72q-42 34 -93 70t-79 52 q-56 -20 -108 6l-93 -66q-47 -34 -75 -56q-30 -22 -78 -70l-90 -88q0 -50 -32 -92l25 -101q13 -51 23 -81q6 -18 17 -44l23 -54l26 -58l26 -56q48 -20 76 -66q0 2 -2 2q54 -4 114 -7t100 -3q54 0 116 4t92 8zM514 120q-4 12 -8 16q4 -4 8 -16zM1110 744q-2 -6 -2 -16 q0 10 2 16zM1106 722q-2 -4 -2 -6q0 2 2 6zM1132 198v2v-2zM1118 206l-2 2zM1104 212q-2 2 -6 2q4 0 6 -2zM1088 218q-2 2 -6 2q4 0 6 -2zM1072 222h-6h6z" /> +<glyph unicode=""" horiz-adv-x="1526" d="M16 338q0 70 26 132t72 108t108 72t132 26h18q10 60 40 111t74 89t100 59t120 21q70 0 131 -26t107 -72t73 -107t27 -129q0 -12 -2 -21t-2 -17q12 4 24 4h22q60 0 114 -23t93 -63t62 -93t23 -113t-23 -114t-62 -93t-93 -62t-114 -23h-22h-684q-6 0 -13 -2t-13 -2 q-70 0 -132 27t-108 73t-72 107t-26 131z" /> +<glyph unicode="#" horiz-adv-x="1520" d="M1286 216v-10q0 -44 -16 -81t-44 -65t-65 -44t-79 -16h-802q-42 0 -80 16t-66 44t-44 65t-16 81v12l114 -68l490 -10l494 10zM1352 592v-206l-66 -66v-80l-116 -68l-484 -10h-8l-488 10l-116 68v72l-76 74v206l76 74v90q0 42 16 79t44 66t66 45t80 16h130l102 104h118v86 q4 34 16 58q4 4 6 10t6 10q6 8 22 20q8 4 28 12q10 2 19 3t19 1h56q16 26 46 26q22 0 38 -16t16 -38t-16 -38t-38 -16q-30 0 -46 26h-56q-12 0 -28 -8q-10 -2 -20 -16t-10 -34v-86h114l102 -104h178q42 0 79 -16t65 -45t44 -66t16 -79v-100zM318 284q22 0 37 15t15 37 t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15zM1024 284q22 0 37 15t15 37t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15z" /> +<glyph unicode="$" horiz-adv-x="1460" d="M642 1288q132 0 249 -51t204 -138t138 -204t51 -249q0 -134 -51 -251t-138 -204t-204 -138t-249 -51q-134 0 -252 51t-205 138t-137 204t-50 251q0 132 50 249t137 204t205 138t252 51zM642 1222q-14 0 -25 -1t-25 -1q-4 -2 -11 -2t-13 -2h-10l-46 -8q-24 -4 -46 -12 q58 4 117 0t113 -32q6 -22 27 -26t44 -5t40 -6t15 -31l4 -4q-2 -2 -1 -6t3 2q8 -8 18 -20t16 -22t4 -16t-18 -4q-22 4 -31 -12t-8 -39t11 -44t26 -29q14 -8 25 -5t15 11t-1 18t-19 18q-2 20 11 17t26 -17t20 -32t-9 -28q-4 -2 -6 -4v2q0 -2 -2 -2q-18 -12 -23 -33t-6 -44 l-2 -47q-1 -24 -7 -44q-12 -28 -1 -37t27 -4t30 20t8 37q-8 20 4 27t28 11t28 8t4 18q24 -12 36 -4t14 25t-4 37t-16 32q6 18 13 19t13 -7t8 -21t-4 -23q2 -20 16 -27t26 -11q2 -2 4 -2q12 -4 16 -14t-6 -32q-16 -48 -57 -57t-81 1q-20 -8 -33 -23t-22 -33t-18 -38t-19 -36 q-12 -22 -21 -46t-12 -49t4 -48t27 -41q18 -18 38 -21t40 4t39 20l37 25q30 4 47 -10t22 -36t-1 -47t-20 -43q-2 -6 -2 -14q64 76 101 171t37 203q0 34 -6 64q-12 -6 -27 5t-23 23t-6 18t24 -12q20 0 26 10q-8 52 -30 104q-6 10 -16 16q2 6 1 12t-1 10v2q-36 72 -89 132 t-120 102t-146 66t-163 24zM398 1146q-34 -28 -73 -64l-78 -75q-39 -39 -72 -81t-53 -84q-18 -52 -19 -105t3 -109q16 -52 49 -98t61 -94q18 -24 15 -49t-5 -49v-2q0 -16 3 -30t15 -28q24 -32 54 -59t46 -67v-2q30 -18 61 -32t65 -26q18 2 35 7t35 9v2q28 10 54 24t48 30 q18 24 13 43t-23 33t-43 19t-43 -1q-44 28 -81 67t-85 61q-22 18 -52 25t-58 15t-47 24t-19 54q4 34 -17 55t-43 37q-8 26 8 44t40 23t45 -6t21 -41q16 0 28 13t24 28l25 30q13 15 31 19q10 8 26 10t29 4t20 7t-3 23q10 8 26 14t29 14t18 19t-9 31q-4 20 -18 35t-30 19 t-31 -3t-23 -31q-22 -6 -30 11t-4 39t20 38t40 10q16 0 24 5t8 14t-6 17t-16 14q0 2 -2 2q-4 2 -16 2q-22 12 -17 33t14 43t11 41t-28 29zM570 1056q-2 0 -3 -1t-3 -1q-10 -2 -14 -9t-2 -13t10 -7t20 11q6 4 8 11t-8 7q-4 2 -8 2zM616 1046q-16 -2 -29 -9t-21 -19t-8 -26 t14 -28q10 -12 6 -30l-8 -35q-4 -17 -1 -32t27 -21q8 20 24 39t29 38t20 41t1 46q-4 22 -21 30t-33 6zM496 978q-6 0 -8 -2q6 -12 4 -24t-5 -24t-3 -24t10 -22q12 6 20 22t8 32t-6 29t-20 13zM830 842q-6 0 -6 -5t12 -15q-10 -22 6 -24t26 14q2 12 -13 21t-25 9zM494 752 q-6 -2 -7 -6t15 -14q2 0 9 1t1 -7q22 14 8 21t-26 5zM372 704q-6 0 -12 -2q2 0 2 -2h2q6 -2 9 1t-1 3zM254 542q-20 -4 12 -24q4 -4 11 -9t15 -1q4 12 -3 20t-17 12q-2 2 -10 2h-8z" /> +<glyph unicode="%" horiz-adv-x="1478" d="M180 1142q32 0 53 -22t21 -52v-146h-146v146q0 30 21 52t51 22zM290 886q30 0 52 -22t22 -54v-72q0 -32 -22 -53t-52 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM254 628v-548q0 -32 -21 -53t-53 -21q-30 0 -51 21t-21 53v548h146zM1104 1142q30 0 52 -22 t22 -52v-146h-148v146q0 30 22 52t52 22zM1214 886q30 0 51 -22t21 -54v-72q0 -32 -21 -53t-51 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM1178 628v-548q0 -32 -22 -53t-52 -21t-52 21t-22 53v548h148zM632 6q-30 0 -52 21t-22 53v146h146v-146 q0 -32 -21 -53t-51 -21zM522 262q-32 0 -53 21t-21 53v72q0 30 21 52t53 22h220q30 0 51 -22t21 -52v-72q0 -32 -21 -53t-51 -21h-220zM558 518v550q0 30 22 52t52 22t51 -22t21 -52v-550h-146z" /> +<glyph unicode="&" d="M1200 276q54 -14 71 -34t11 -44t-30 -48t-54 -46t-60 -38l-46 -24q-26 -12 -83 -25t-125 -25t-135 -20t-111 -8t-112 9t-136 21t-124 25t-76 23q-12 4 -38 20t-56 38t-56 47t-34 49t7 45t69 35q80 20 121 68t59 114t20 142t2 154q0 76 38 128t84 84q52 36 122 58 q-2 44 10 80q10 30 34 56t70 26t70 -26t34 -56q10 -36 10 -80q68 -22 122 -58q46 -32 84 -84t38 -128q0 -78 2 -154t20 -142t59 -114t119 -68zM660 88q92 0 175 11t146 26t99 33t36 32q0 12 -27 23t-74 21t-112 16t-139 10q0 -42 -29 -71t-71 -29t-71 29t-29 71 q-76 -4 -140 -10t-111 -16t-74 -21t-27 -23q0 -14 35 -32t95 -33t142 -26t176 -11z" /> +<glyph unicode="'" horiz-adv-x="1252" d="M1188 2h-1186l206 776h140q-32 42 -50 91t-18 105q0 66 25 123t67 99t100 67t124 25t123 -25t100 -67t68 -99t25 -123q0 -108 -66 -196h138zM596 1086q-46 0 -80 -33t-34 -79q0 -48 34 -82t80 -34q48 0 81 34t33 82q0 46 -33 79t-81 33z" /> +<glyph unicode="(" horiz-adv-x="1198" d="M34 506q-18 22 -27 50t-9 57t9 55t27 44l528 432h58q58 0 92 -22t34 -78v-106q0 -20 6 -36t42 -16h306q36 0 68 -31t32 -69v-354q0 -40 -32 -70t-68 -30h-306q-36 0 -42 -14t-6 -34v-100q0 -60 -34 -95t-92 -35q-10 6 -18 12q-8 4 -18 8t-22 4z" /> +<glyph unicode=")" horiz-adv-x="1198" d="M1158 698q18 -22 27 -50t9 -57t-9 -55t-27 -44l-528 -432h-58q-58 0 -92 22t-34 78v106q0 20 -6 36t-42 16h-306q-36 0 -68 31t-32 69v354q0 40 32 70t68 30h306q36 0 42 14t6 34v100q0 60 34 95t92 35l16 -12q8 -4 19 -8t23 -4z" /> +<glyph unicode="*" horiz-adv-x="1638" d="M1532 1222q16 0 28 -12t12 -30v-1080q0 -16 -12 -28t-28 -12h-1492q-16 0 -28 12t-12 28v1080q0 18 12 30t28 12h1492zM588 750q10 10 8 20q4 14 -8 22l-94 94q-4 2 -5 4t-3 2l-206 206q-20 16 -40 0l-96 -96q-6 -8 -6 -20t6 -18l192 -194l-192 -190q-6 -8 -6 -20t6 -20 l96 -96q20 -16 40 0l206 208q4 0 8 4zM1302 448q0 16 -12 28t-28 12h-582q-16 0 -28 -12t-12 -28v-90q0 -16 12 -29t28 -13h582q16 0 28 13t12 29v90z" /> +<glyph unicode="+" horiz-adv-x="1340" d="M1010 1280q62 0 105 -20t69 -51t38 -72t12 -83v-840q0 -42 -12 -81t-38 -71t-69 -51t-105 -19h-786q-42 0 -81 19t-71 51t-51 71t-19 81v840q0 42 19 83t51 72t71 51t81 20h786zM1010 720h-280v334h-226v-334h-280v-226h280v-280h226v280h280v226z" /> +<glyph unicode="," horiz-adv-x="1328" d="M1072 566l146 -146q28 -28 42 -65t14 -75t-14 -74t-42 -62l-72 -72q-28 -28 -64 -42t-73 -14t-74 14t-65 42l-146 146zM622 1016l358 -360l-348 -346l-358 358zM384 628q16 -16 36 -16t36 16q14 12 14 33t-14 35q-16 16 -36 15t-36 -17q-14 -12 -14 -33t14 -33zM662 490 q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -34q14 -16 35 -16t33 16q16 14 16 34t-16 36zM768 594q-16 14 -36 14t-36 -14q-14 -16 -14 -37t14 -37q16 -12 36 -12t36 14q14 16 14 36t-14 36zM558 594q-14 14 -34 14t-34 -14q-16 -16 -16 -37t16 -37q12 -12 33 -12t35 12 q16 16 16 37t-16 37zM870 694q-14 16 -34 17t-36 -15q-14 -14 -14 -35t14 -33q16 -16 36 -16t34 16q16 12 16 33t-16 33zM662 696q-12 16 -33 16t-35 -16q-16 -14 -16 -35t16 -33q14 -16 35 -16t33 16q14 12 14 33t-14 35zM768 800q-16 16 -36 16t-36 -16q-14 -14 -14 -35 t12 -33q16 -16 37 -16t37 16q14 14 14 34t-14 34zM558 800q-14 16 -34 16t-34 -16q-16 -14 -16 -34t16 -32q14 -16 34 -16t34 16q16 12 16 32t-16 34zM662 834q16 16 16 36t-16 36q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -36q14 -14 35 -14t33 14zM144 1218q26 28 62 42 t74 14t75 -15t67 -43l108 -112l-344 -344l-114 108q-28 30 -42 66t-14 74t14 74t42 64z" /> +<glyph unicode="-" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" /> +<glyph unicode="." horiz-adv-x="1072" d="M0 1184q0 44 31 75t77 31h642q46 0 77 -31t31 -75v-1074q0 -44 -31 -75t-77 -31h-642q-46 0 -77 31t-31 75v1074zM194 1074q-24 0 -42 -18t-18 -42v-692q0 -26 18 -43t42 -17h470q24 0 42 17t18 43v692q0 24 -18 42t-42 18h-470zM362 144q0 -28 20 -48t48 -20t47 20 t19 48t-19 47t-47 19t-48 -19t-20 -47zM542 1158q0 28 -28 28h-170q-10 0 -19 -8t-9 -20q0 -10 9 -19t19 -9h170q12 0 20 9t8 19z" /> +<glyph unicode="/" horiz-adv-x="1224" d="M904 274q16 -14 16 -34t-16 -36q-14 -14 -34 -14t-34 14l-92 92q-4 -4 -9 -10t-9 -10q-22 -22 -45 -37t-47 -25l-2 -88q-2 -12 -9 -27t-17 -25l-62 -64q-16 -16 -36 -16t-34 16q-16 16 -16 36t16 34l42 42q10 12 12 22l2 44q-44 -4 -89 0t-87 30q-2 0 -4 2q-4 2 -8 6 l-54 54l-4 4l-54 54q-6 2 -6 8l-4 4q-24 44 -27 89t-1 87l-44 -2q-14 0 -22 -12l-42 -42q-16 -14 -35 -14t-35 14q-16 16 -16 35t16 35l62 62q10 10 25 17t27 7l88 4q24 52 64 92l9 9q5 5 11 9l-92 92q-14 14 -14 34t14 36q14 14 34 14t36 -14l48 -50v2l66 -66q14 4 27 7 l29 7l4 120q0 14 9 30t19 26l64 64q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-44 -46q-12 -12 -14 -22l-2 -76q34 10 69 28t69 52q38 40 91 46t99 -22l-64 -64q-12 -12 -13 -28t11 -26q0 -2 2 -4q12 -10 28 -10t26 10l66 68q28 -46 22 -99t-46 -93q-34 -34 -52 -68 t-30 -70l78 4q8 0 20 12l48 46q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-62 -64q-10 -10 -27 -18t-29 -8l-120 -6q-4 -14 -7 -27l-7 -29l64 -64z" /> +<glyph unicode="0" horiz-adv-x="1114" d="M128 1250q30 8 60 3t56 -21t45 -42t23 -56q4 -48 5 -97t1 -97q124 0 246 2t244 -4q46 -8 83 -41t43 -81q8 -30 2 -61t-23 -57t-43 -45t-56 -23q-126 -10 -250 -7t-250 1q2 -46 2 -91t6 -91q8 -44 41 -79t79 -39q94 -8 187 -5t187 -5q40 -6 69 -34t43 -65t9 -77t-33 -70 q-14 -26 -37 -40t-49 -20t-54 -7t-54 -1q-96 2 -193 -1t-189 25q-70 20 -130 64t-103 102t-68 127t-25 143q-4 150 -3 300t1 300q-4 32 3 64t24 58t43 45t58 23z" /> +<glyph unicode="1" horiz-adv-x="1198" d="M874 774q-20 -10 -28 -26t-9 -34t4 -36t11 -36q6 -16 19 -33t21 -33q12 -28 14 -59t-2 -63q-4 -28 -18 -52t-34 -45t-42 -40l-42 -35q-24 -18 -48 -26t-44 -16t-34 -23t-18 -49t-6 -48t-3 -18t-1 -6t-2 -14q0 -2 16 -4t37 -4l41 -4q20 -2 28 -6t21 -12t24 -17t19 -18 t8 -11q0 -4 -45 -5t-100 -1t-102 1t-53 5q-6 2 2 13t19 23t21 24t10 16t-1 23l-2 40q-1 21 -5 40t-10 23q-14 10 -42 16t-58 16q-44 14 -98 11t-100 -3q-70 0 -141 -12t-141 -12l-40 60q62 8 114 16l78 11q36 5 36 9t-24 14t-52 20q-34 12 -74 28l64 16q24 6 42 10t14 2 q58 22 123 30t123 32q16 6 52 32t76 60t75 68t47 56l22 43l19 37q9 18 17 37t14 45l15 65q7 31 39 49q28 18 64 18t68 -8q24 -6 53 -31t56 -56t46 -60t23 -43q2 -4 -23 -5t-55 -1t-58 1t-32 -1q-4 0 -8 -4z" /> +<glyph unicode="2" horiz-adv-x="1460" d="M1282 1282v-1280h-1282v1280h1282zM1106 1190h-174q-70 -2 -115 -22t-71 -52t-36 -76t-10 -94v-110h-116v-196h116v-548h232v548h152l22 196h-174v110q0 26 12 42t42 16h120v186z" /> +<glyph unicode="3" d="M1024 976q78 0 146 -29t119 -80t81 -118t30 -143q0 -78 -30 -147t-81 -120t-119 -81t-146 -30q-102 0 -188 52t-132 138q-52 -86 -137 -138t-189 -52q-78 0 -146 30t-119 81t-80 120t-29 147q0 76 29 143t80 118t119 80t146 29q104 0 189 -51t137 -135q46 84 132 135 t188 51zM1218 410q82 76 82 196q0 110 -82 192q-36 36 -86 57t-108 21q-56 0 -105 -21t-85 -57q-40 -40 -61 -89t-21 -103q0 -58 21 -109t61 -87q74 -80 190 -80q58 0 108 20t86 60z" /> +<glyph unicode="4" horiz-adv-x="1466" d="M0 1284h1286v-1286h-1286v1286zM1170 114v528h-526v526h-526v-528h526v-526h526z" /> +<glyph unicode="5" horiz-adv-x="1240" d="M314 1072q0 44 28 66q24 26 72 26q42 0 66 -26q26 -24 26 -68v-186q0 -22 -5 -40t-21 -30q-26 -26 -70 -26q-42 0 -68 26q-28 22 -28 72v186zM382 880q0 -16 8 -24q4 -4 20 -4t20 4q10 10 10 24v194q0 14 -10 20q-8 8 -20 8q-6 0 -10 -2t-10 -6q-8 -6 -8 -20v-194z M156 988l-90 294h76l48 -196h6l46 196h76l-86 -282v-202h-76v190zM610 794q-26 0 -34 14q-6 10 -10 20t-4 28v298h68v-276q0 -8 4 -16q2 -6 16 -6q6 0 18 6q8 12 18 18v274h68v-356h-68v36l-18 -18q-8 -8 -18 -14q-22 -8 -40 -8zM1082 524q4 -50 6 -101t2 -101 q0 -54 -2 -104t-6 -102q0 -66 -47 -111t-111 -45q-94 -4 -188 -7t-192 -3q-100 0 -194 3t-190 7q-64 0 -111 45t-47 111q-4 52 -6 102t-2 104q0 50 2 101t6 101q0 32 13 61t34 51t50 35t61 13q188 6 384 6q194 0 380 -6q32 0 61 -13t50 -35t34 -51t13 -61zM232 500h76v68 h-226v-68h76v-416h74v416zM500 442h-70v-270q-12 -16 -20 -22q-10 -6 -16 -6q-12 0 -16 4q-4 8 -4 20v274h-66v-298q0 -32 12 -50q12 -14 34 -14q20 0 40 10q10 4 19 14t17 20v-40h70v358zM754 356q0 44 -18 68q-16 22 -46 22q-16 0 -30 -8q-18 -6 -30 -26v156h-68v-484h68 v30q6 -10 14 -16t16 -10q14 -8 32 -8q30 0 46 20q8 12 12 25t4 33v198zM998 346q0 54 -22 78q-28 28 -72 28t-72 -28q-12 -16 -20 -35t-8 -43v-160q0 -50 26 -82q22 -28 68 -28q52 0 76 24q24 30 24 86v28h-68v-26q0 -16 -1 -27t-5 -15q-8 -8 -24 -8q-10 0 -22 8 q-6 12 -6 42v66h126v92zM900 388q18 0 24 -6q6 -16 6 -36v-36h-58v36q0 20 6 36q8 6 22 6zM660 390q12 0 20 -8q4 -8 4 -28v-184q0 -24 -4 -26q-8 -8 -20 -8q-4 0 -16 6q-8 0 -14 10v224q4 4 14 12q4 0 8 1t8 1z" /> +<glyph unicode="6" horiz-adv-x="1442" d="M1214 802q-12 -178 -248 -482q-250 -320 -420 -320q-102 0 -178 196l-48 178l-50 178q-54 196 -116 196q-16 0 -96 -56l-58 74q46 38 89 78l89 80q58 52 104 78t74 30q140 12 176 -194q18 -112 30 -180t16 -96q42 -184 92 -184q36 0 114 118q74 122 82 182 q10 104 -82 104q-22 0 -45 -5t-47 -15q92 290 344 284q186 -8 178 -244z" /> +<glyph unicode="7" horiz-adv-x="1466" d="M1174 1284q46 0 78 -32t32 -78v-1066q0 -46 -32 -78t-78 -32h-1066q-46 0 -78 32t-32 78v1066q0 46 32 78t78 32h1066zM952 352q-74 -50 -152 -50q-46 0 -76 22q-24 16 -36 38q-8 24 -8 112v260h240v160h-240v256h-144q-12 -78 -36 -130q-14 -26 -31 -48t-39 -38 q-42 -38 -100 -56v-144h110v-354q0 -68 16 -106t54 -68q18 -16 40 -29t50 -23q54 -16 122 -16q60 0 112 12q28 4 57 16t61 28v158z" /> +<glyph unicode="8" horiz-adv-x="1454" d="M682 702q0 2 2 2v-2h-2zM1180 1282q38 0 66 -26t28 -66v-1100q0 -38 -28 -65t-66 -27h-1088q-40 0 -67 27t-27 65v1100q0 40 27 66t67 26h1088zM382 788h-190v-580h190v580zM288 866q50 0 79 29t29 73q0 40 -29 69t-79 29q-48 0 -78 -29t-30 -69q0 -44 29 -73t77 -29h2z M1078 540q0 134 -63 198t-159 64q-38 0 -66 -10t-48 -24t-34 -32l-24 -32v84h-194q0 -10 1 -41t1 -74v-94v-103q0 -122 -2 -268h194v324v24q0 12 4 22q10 26 35 49t65 23q54 0 76 -37t22 -95v-310h192v332z" /> +<glyph unicode="9" horiz-adv-x="1310" d="M1162 514q24 -40 38 -85t14 -95q0 -70 -27 -132t-73 -108t-108 -73t-134 -27q-50 0 -94 14t-84 38q-40 -8 -88 -8q-118 0 -220 44t-179 121t-121 179t-44 220q0 48 8 86q-24 40 -38 85t-14 95q0 72 27 134t73 108t108 73t132 27q50 0 95 -14t85 -38q38 8 88 8 q116 0 219 -44t180 -121t121 -180t44 -219q0 -50 -8 -88zM874 344q16 28 26 56t10 62q0 52 -22 90q-16 36 -56 62q-40 24 -90 42q-26 8 -55 15l-61 15l-44 10q-18 4 -30 8q-20 6 -44 16q-22 12 -32 28q-12 12 -12 34q0 34 36 56q30 22 94 22t92 -20q14 -10 26 -26t22 -34 q14 -30 30 -42q18 -14 42 -14q32 0 50 22q20 20 20 48q0 14 -4 28t-10 30q-16 26 -52 56q-32 26 -86 44q-52 16 -120 16q-90 0 -152 -24q-68 -28 -102 -70q-32 -44 -32 -106q0 -64 30 -102q38 -44 90 -66q54 -24 138 -42q28 -8 53 -13t43 -11q40 -14 58 -34q24 -22 24 -54 q0 -42 -44 -70q-42 -32 -108 -32q-48 0 -80 16q-28 16 -46 36q-8 10 -15 25l-15 31q-14 30 -30 44t-42 14q-32 0 -52 -18q-20 -24 -20 -46q0 -24 8 -47t24 -47q32 -46 86 -76q76 -40 186 -40q92 0 164 30q34 14 59 34t45 44z" /> +<glyph unicode=":" horiz-adv-x="1382" d="M1002 390l128 -16l-14 -112l-128 16zM1028 462l-2 504l218 -26l-122 -490zM466 956q108 0 194 -28t148 -77t95 -114t33 -139t-34 -139t-96 -113t-149 -76t-191 -28t-190 28t-148 76t-96 113t-34 139t33 139t94 114t148 77t193 28zM754 710q-8 0 -23 1t-32 1h-33h-28h-33 h-37h-32h-20l4 -28q6 0 34 -3t40 -7q-2 -8 -18 -23l-35 -32q-19 -17 -37 -31t-22 -20q-16 22 -54 70t-68 94q6 2 18 3t26 1t26 1t16 3l4 20q-4 0 -22 1t-40 1h-45h-37q-46 -4 -86 -1t-46 -1v-24q6 0 18 -2t25 -3t23 -3t14 -4q12 -10 38 -38t52 -60t46 -59t22 -33 q2 -10 2 -21v-17v-16v-37q0 -9 -4 -13q-10 -10 -39 -10h-39l-6 -26h26q18 0 37 1t36 1h27h144v26h-17h-27q-14 0 -27 1t-15 1q-2 10 -4 26t-2 28v20q0 20 4 36q2 10 26 32t52 45t53 41t31 20l34 7q24 5 32 5z" /> +<glyph unicode=";" horiz-adv-x="1204" d="M594 628q22 -12 36 -35t14 -51q0 -26 -14 -48t-36 -36v-64h-98v64q-22 14 -36 36t-14 48q0 28 14 51t36 35v212h98v-212zM978 870q52 -68 82 -151t30 -177q0 -112 -43 -211t-117 -173t-174 -117t-212 -43t-211 43t-173 117t-117 173t-43 211q0 108 38 202t105 167 t158 119t195 56v102h-50v98h198v-98h-50v-102q78 -8 149 -36t129 -72l110 108l106 -106zM544 98q92 0 173 35t142 95t96 141t35 173t-35 173t-96 142t-142 96t-173 35t-173 -35t-141 -96t-95 -142t-35 -173t35 -173t95 -141t141 -95t173 -35z" /> +<glyph unicode="<" horiz-adv-x="1198" d="M614 1290q78 0 145 -29t118 -80t80 -119t29 -146q0 -50 -18 -138t-47 -190t-68 -207t-81 -190t-85 -139t-79 -54q-44 0 -89 54t-85 139t-75 190t-61 207t-41 189t-15 139q0 78 29 146t80 119t118 80t145 29zM614 828q58 0 100 42t42 100t-42 100t-100 42t-100 -42 t-42 -100t42 -100t100 -42z" /> +<glyph unicode="=" horiz-adv-x="1400" d="M1270 930q34 0 59 -25t25 -61v-650q0 -34 -25 -58t-59 -24h-1186q-34 0 -58 24t-24 58v650q0 36 24 61t58 25h252l54 102q0 12 9 23t25 11h510q12 0 22 -11t10 -23l52 -102h252zM676 192q70 0 132 27t108 73t73 107t27 131t-27 132t-73 108t-108 73t-132 27t-131 -27 t-107 -73t-73 -108t-27 -132t27 -131t73 -107t107 -73t131 -27zM886 530q0 -42 -17 -80t-45 -67t-66 -45t-82 -16q-42 0 -80 16t-67 45t-45 67t-16 80q0 44 16 82t45 66t67 45t80 17q44 0 82 -17t66 -45t45 -66t17 -82z" /> +<glyph unicode=">" horiz-adv-x="1204" d="M146 16q-24 -22 -57 -22t-59 22q-22 26 -22 59t22 57l1028 1024q24 22 58 22t58 -22q22 -24 22 -58t-22 -58zM530 12q-22 -22 -56 -22t-58 22q-24 24 -24 58t24 58l650 652q26 24 59 24t57 -24q22 -22 22 -56t-22 -58zM940 30q-24 -22 -58 -22t-56 22q-22 26 -22 59 t22 57l228 228q24 24 58 24t58 -24q22 -22 22 -56t-22 -56z" /> +<glyph unicode="?" horiz-adv-x="1400" d="M646 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM650 258q6 42 -14 76q-24 32 -64 42q-44 10 -76 -14q-38 -22 -44 -62q-10 -44 14 -76 q24 -36 64 -44q44 -10 76 14q36 24 44 64zM978 776q18 80 -12 152q-26 70 -90 118q-56 46 -144 64q-42 10 -94 6q-50 -2 -102 -22q-26 -10 -46 -23t-38 -29q-36 -36 -44 -82q-8 -40 6 -70q18 -30 52 -38q12 -4 25 -1t23 7q20 12 54 36q16 12 31 22t31 16q26 10 64 4 q44 -8 68 -44q28 -34 18 -74q-12 -52 -42 -66q-26 -16 -82 -22q-30 -2 -52 -3t-34 -5q-24 -6 -30 -36l-26 -128q-8 -38 12 -68q18 -28 60 -38q38 -8 72 10q30 20 38 58l10 46q106 2 180 56q72 56 92 154z" /> +<glyph unicode="@" d="M1368 948q28 0 47 -18t19 -46v-818q0 -28 -19 -47t-47 -19h-1304q-28 0 -47 19t-19 47v818q0 28 19 46t47 18h1304zM1366 806q16 14 20 34t-10 36q-14 20 -36 22t-38 -8l-584 -446l-588 438q-20 12 -40 8t-34 -20q-14 -18 -10 -39t20 -33l620 -458q12 -12 32 -12 q18 0 30 12z" /> +<glyph unicode="A" horiz-adv-x="882" d="M178 1260q12 10 27 10q9 0 19 -4q22 -10 28 -32l88 -264q10 -16 0 -36q-8 -20 -24 -26l-44 -20q-22 -10 -30 -30t2 -42l32 -92l28 -79q14 -41 28 -69q12 -28 31 -63l35 -67l42 -76q8 -22 29 -30t43 2l44 20q16 10 38 0q20 -10 26 -24l138 -242q7 -12 7 -24q0 -10 -5 -20 q-12 -22 -32 -30h-6q-48 -12 -112 -20q-17 -2 -33 -2q-45 0 -85 16q-36 14 -75 49t-77 86t-74 114t-66 131l-36 76q-4 4 -4 5t-2 5l-9 21q-9 21 -19 47t-20 47t-10 23q-30 68 -52 137t-34 131q-13 68 -13 122q0 48 13 82q10 26 29 51t41 46t46 38l44 31z" /> +<glyph unicode="B" horiz-adv-x="1466" d="M1234 742q18 0 34 -16t16 -34v-100q0 -22 -16 -36t-34 -14h-60q-20 0 -39 -13t-27 -33l-34 -84q-8 -20 -5 -42t19 -38l40 -40q16 -16 16 -35t-16 -35l-70 -70q-16 -16 -35 -16t-35 16l-40 40q-16 16 -38 19t-42 -5l-80 -34q-22 -8 -36 -27t-14 -39v-60q0 -18 -14 -34 t-36 -16h-100q-18 0 -31 16t-13 34v60q0 20 -16 39t-34 27l-80 34q-20 8 -42 5t-38 -19l-40 -40q-16 -16 -38 -16t-32 16l-70 70q-16 16 -16 35t16 35l40 40q16 16 18 38t-8 42l-30 84q-8 20 -27 33t-39 13h-60q-18 0 -34 14t-16 36v100q0 18 16 34t34 16l60 -6q20 0 39 16 t27 34l30 80q10 20 8 42t-18 38l-40 46q-16 10 -16 32t16 38l70 64q10 16 32 16t38 -16l40 -40q16 -16 38 -18t42 8l80 36q18 4 34 22t16 38v60q0 22 13 36t31 14h100q50 0 50 -50v-60q0 -20 14 -38t36 -22l80 -36q20 -10 42 -8t38 18l40 40q16 16 35 16t35 -16l70 -64 q16 -16 16 -38t-16 -32l-40 -46q-16 -16 -19 -38t5 -42l34 -80q8 -18 27 -31t39 -13h60zM888 642q0 50 -19 95t-53 78t-80 52t-98 19q-50 0 -95 -19t-78 -52t-52 -78t-19 -95q0 -52 19 -98t52 -80t78 -53t95 -19q52 0 98 19t80 53t53 80t19 98z" /> +<glyph unicode="C" horiz-adv-x="1574" d="M1224 1280q38 0 71 -14t58 -39t39 -58t14 -71v-612q0 -74 -53 -127t-129 -53h-672v-318l-356 318h-14q-38 0 -72 14t-59 39t-39 58t-14 69v612q0 38 14 71t39 58t59 39t72 14h1042z" /> +<glyph unicode="D" horiz-adv-x="1466" d="M820 470v414q0 34 -24 58t-58 24h-12q-34 0 -59 -24t-25 -58v-414q0 -34 25 -58t59 -24h12q34 0 58 24t24 58zM820 214q0 38 -27 63t-63 25q-38 0 -64 -25t-26 -63q0 -40 26 -66t64 -26q36 0 63 26t27 66zM1358 -2h-1258q-58 0 -79 38q-9 16 -9 34q0 26 18 56l628 1086 q28 54 71 54t75 -54l626 -1086q19 -31 19 -58q0 -17 -8 -32q-21 -38 -83 -38z" /> +<glyph unicode="E" horiz-adv-x="1502" d="M1152 842l-322 326l102 106q22 18 53 18t53 -18l220 -220q22 -22 22 -53t-22 -53zM-4 48l58 310q0 30 22 56l678 676l324 -326l-676 -676q-6 -6 -22 -14t-22 -12l-322 -58q-5 -1 -9 -1q-14 0 -23 10t-9 25q0 4 1 10z" /> +<glyph unicode="F" horiz-adv-x="1514" d="M1114 1416l4 -96l-88 34l-88 -34l6 96l-62 74l94 24l50 78l50 -78l92 -24zM770 1512l8 -142l-130 50l-132 -50l10 142l-92 108l138 36l76 118l76 -118l134 -36zM1240 1062l-178 178q-18 18 -42 18t-44 -18l-262 -260h4q17 0 56 -26q44 -30 98 -82q52 -52 82 -97 q24 -36 24 -52q0 -4 -2 -7l264 260q18 20 18 44t-18 42zM812 814q-56 54 -101 85q-34 23 -48 23q-5 0 -7 -2l-4 -2l-620 -620v-4l-12 -10q-16 -18 -16 -42t16 -42l180 -178q16 -18 41 -18t41 18l60 56h-4l580 578q2 2 2 7q0 14 -22 50q-30 47 -86 101z" /> +<glyph unicode="G" d="M642 1288q134 0 252 -51t205 -140t138 -207t51 -252q0 -132 -51 -250t-138 -206t-205 -139t-252 -51t-252 51t-207 139t-140 206t-51 250q0 134 51 252t140 207t207 140t252 51zM642 202q90 0 169 35t138 94t94 138t35 169t-35 170t-94 140t-138 94t-169 34t-170 -34 t-140 -94t-94 -140t-34 -170t34 -169t94 -138t140 -94t170 -35zM878 638q0 -48 -19 -91t-51 -75t-75 -51t-91 -19q-50 0 -93 19t-76 51t-52 75t-19 91q0 50 19 93t52 76t76 52t93 19q48 0 91 -19t75 -52t51 -76t19 -93z" /> +<glyph unicode="H" horiz-adv-x="1300" d="M1198 1282q36 0 63 -27t27 -63l-46 -466q-4 -26 -18 -40q-22 -22 -52 -22t-50 22l-52 50q-26 20 -52 0l-216 -220q-22 -22 -51 -22t-51 22l-178 178q-22 22 -22 52t22 52l218 216q10 10 10 25t-10 25l-50 52q-22 22 -22 50t22 50q14 18 40 22zM892 416q10 6 14 8l102 104 v-476q0 -22 -16 -38t-40 -16h-900q-22 0 -38 16t-16 38v900q0 24 16 40t38 16h488l-108 -108l-8 -8h-272q-16 0 -27 -11t-11 -27v-702q0 -18 11 -28t27 -10h704q16 0 26 10t10 28v264z" /> +<glyph unicode="I" horiz-adv-x="1204" d="M100 96q-50 50 -75 112t-25 127t25 127t75 112q70 70 161 93q41 10 82 10q50 0 99 -15l418 418l202 -22l22 -200l-158 -40l-22 -140l-150 -32l-18 -136l-72 -72q16 -50 16 -101q0 -40 -10 -80q-22 -91 -92 -161q-50 -50 -112 -75t-127 -25t-127 25t-112 75zM332 330 q-34 34 -83 34t-83 -34t-34 -82t34 -82t83 -34t83 34t34 82t-34 82z" /> +<glyph unicode="J" horiz-adv-x="1460" d="M638 1288q134 0 251 -51t205 -139t139 -205t51 -251t-51 -251t-139 -205t-205 -139t-251 -51t-251 51t-205 139t-139 205t-51 251t51 251t139 205t205 139t251 51zM638 1124q-30 0 -51 -20t-21 -52v-306q-54 -34 -54 -104q0 -54 36 -93t90 -39t92 39t38 93q0 34 -15 61 t-39 43v306q0 32 -23 52t-53 20zM810 574q-28 -76 -104 -104l104 -104q22 -24 52 -24t52 24q22 22 22 53t-22 51z" /> +<glyph unicode="K" horiz-adv-x="1370" d="M442 156l-282 282v402l282 284h402l284 -284v-402l-284 -282h-402zM878 74l332 332v468l-332 332h-468l-332 -332v-468l332 -332h468zM348 26l-318 318q-12 14 -20 33t-8 35v454q0 16 8 36t20 32l318 320q14 10 34 18t34 8h454q16 0 36 -8t32 -18l316 -320q12 -12 22 -32 t10 -36v-454q0 -16 -10 -35t-22 -33l-316 -318q-12 -12 -32 -20t-36 -8h-454q-14 0 -34 8t-34 20z" /> +<glyph unicode="L" horiz-adv-x="1442" d="M642 968q52 80 121 120t141 46q14 1 27 1q58 0 114 -21q69 -26 123 -77t87 -123t33 -160q0 -90 -36 -169t-91 -146t-123 -123l-130 -104l-106 -81l-71 -56l-50 -41q-21 -18 -39 -38q-20 20 -42 38l-52 41l-72 55l-104 82l-129 105q-67 57 -123 123t-92 143t-36 167 q0 88 33 161t87 125t124 78q57 21 115 21q13 0 27 -1q72 -6 141 -46t123 -120z" /> +<glyph unicode="M" horiz-adv-x="1484" d="M1060 444l26 -444l-416 162l-418 -162l26 444l-282 348l432 114l242 378l244 -378l432 -114z" /> +<glyph unicode="N" d="M744 726q48 0 83 -36t35 -88q0 -50 -35 -86t-83 -36q-52 0 -89 36t-37 86q0 52 37 88t89 36zM744 1094q76 0 148 -21t138 -56t124 -79t108 -90q116 -110 214 -246q-98 -138 -214 -246q-50 -46 -108 -90t-124 -78t-138 -55t-148 -21t-148 21t-138 55t-125 78t-109 90 q-118 108 -216 246q98 136 216 246q50 46 109 90t125 79t138 56t148 21zM744 298q62 0 118 24t98 65t66 96t24 119t-24 120t-66 97t-98 65t-118 24q-66 0 -122 -24t-97 -65t-65 -97t-24 -120t24 -119t65 -96t97 -65t122 -24z" /> +<glyph unicode="O" horiz-adv-x="858" d="M276 -2v152q-80 2 -151 22t-117 46l48 184q50 -28 117 -49t145 -21q70 0 111 26t41 72t-39 77t-129 61q-128 44 -207 109t-79 179q0 108 70 184t198 98v152h156v-140q78 -4 134 -18t96 -34l-48 -180q-30 16 -86 36t-142 20q-76 0 -105 -28t-29 -62q0 -38 44 -68t146 -68 q142 -48 203 -118t61 -178q0 -52 -18 -100t-54 -86t-88 -65t-120 -39v-164h-158z" /> +<glyph unicode="P" horiz-adv-x="1460" d="M832 670q-16 -10 -36 -10q-26 0 -43 17t-17 39v104q-60 -2 -113 -25t-93 -63t-63 -93t-23 -113q0 -62 23 -116t63 -94t94 -63t116 -23q54 0 102 18t87 50t65 76t36 96q4 20 25 38t53 18h140q22 0 37 -17t15 -39v-14q-14 -104 -63 -194t-124 -157t-170 -105t-203 -38 q-118 0 -220 44t-179 121t-121 179t-44 220q0 116 44 218t120 178t178 120t218 44v104q0 26 17 41t43 15q10 0 19 -1t17 -7l306 -230q27 -28 27 -55.5t-27 -54.5z" /> +<glyph unicode="Q" horiz-adv-x="1258" d="M204 402q16 -20 12 -46t-28 -40q-20 -16 -46 -11t-42 23q-46 70 -72 148t-26 162v27q0 13 4 27v10q0 6 3 14t3 18v4q4 8 4 13t4 9q0 8 2 17t2 15q4 4 5 8t5 10q0 16 8 32q6 6 6 8v2q12 18 18 40l2 2v2q20 34 43 65t51 59q8 8 16 15t20 15q4 4 6 10q8 4 15 9l15 11h2 q136 94 306 94v108l252 -170l-252 -176v112q-76 0 -140 -26t-116 -66q-2 0 -3 -2t-5 -2l-13 -13q-5 -5 -13 -11v-4q-22 -22 -38 -42t-32 -48h-2q-6 -8 -8 -17t-6 -15v-4q-8 -20 -14 -32v-10q-4 -6 -7 -13t-3 -15t-4 -12q0 -20 -4 -26v-16v-32q-1 -11 -1 -22q0 -52 16 -103 q19 -61 55 -115zM542 -4l-258 170l258 174v-110q70 0 137 25t121 67v4q40 30 69 68t45 86q4 8 7 17l5 15q0 2 4 10q0 4 3 11t3 13t2 8t2 6q0 8 2 14t2 14v8v38q1 12 1 23q0 115 -73 217q-14 22 -9 47t29 37q16 16 36 16q5 0 10 -1q26 -5 42 -25q96 -142 96 -310v-10v-19 q0 -11 -4 -21v-18q0 -8 -2 -13t-2 -13v-10q-6 -6 -6 -20q-4 -6 -4 -14t-4 -16q-4 -4 -4 -12q-4 -10 -7 -21t-7 -21l-4 -6q-50 -118 -150 -198l-6 -4q-6 -8 -13 -12t-19 -12l-2 -4q-64 -46 -141 -69t-159 -23v-106z" /> +<glyph unicode="R" horiz-adv-x="1328" d="M1248 914q24 -24 24 -57t-24 -57l-322 -268q-18 -12 -40 -12q-28 0 -46 18t-18 46v64q0 14 -10 23t-22 9h-194q-28 0 -47 18t-19 46v226q0 28 19 46t47 18h194q12 0 22 10t10 24v64q0 26 18 45t46 19q24 0 40 -14zM350 666q12 14 36 14q28 0 46 -19t18 -45v-66 q0 -12 10 -22t24 -10h194q26 0 45 -18t19 -46v-226q0 -28 -19 -46t-45 -18h-194q-14 0 -24 -9t-10 -23v-64q0 -28 -18 -46t-46 -18q-24 0 -36 12l-326 268q-24 24 -24 57t24 57z" /> +<glyph unicode="S" d="M1184 1288q42 0 72 -30t30 -74v-1082q0 -42 -30 -72t-72 -30h-1084q-42 0 -72 30t-30 72v1082q0 44 30 74t72 30h1084zM244 836q0 -28 17 -45t43 -17h674q28 0 45 17t17 45v388v2h-796v-2v-388zM1140 594q0 20 -15 34t-37 14h-894q-22 0 -36 -14t-14 -34v-446 q0 -20 14 -34t36 -14h894q22 0 37 14t15 34v446zM496 868q0 -10 -8 -19t-20 -9h-106q-10 0 -19 9t-9 19v280q0 12 9 20t19 8h106q28 0 28 -28v-280zM1082 530q0 -24 -26 -24h-830q-26 0 -26 24q0 26 26 26h830q26 0 26 -26zM1082 376q0 -26 -26 -26h-830q-26 0 -26 26t26 26 h830q26 0 26 -26zM1082 212q0 -10 -8 -17t-18 -7h-830q-10 0 -18 7t-8 17t8 19t18 9h830q10 0 18 -9t8 -19z" /> +<glyph unicode="T" horiz-adv-x="1358" d="M1026 840q40 38 40 93t-40 95q-38 38 -92 38t-92 -38q-40 -40 -40 -95t40 -93q38 -40 92 -40t92 40zM36 588l640 636q16 18 37 24t43 4q6 2 20 2l400 -42h6q34 0 56 -20q19 -18 19 -45q0 -7 -1 -15l14 -368q-2 -8 -7 -21t-11 -26t-13 -25t-13 -18l-640 -638 q-28 -28 -65 -28t-65 28l-418 418q-28 28 -28 67t26 67z" /> +<glyph unicode="U" horiz-adv-x="1400" d="M874 492q-12 -24 -5 -53t19 -59l24 -60q12 -30 11 -54t-20 -40t-67 -18q-9 -1 -18 -1q-51 0 -105 20q-63 23 -122 57t-107 72t-74 62h-2q-4 2 -8 6t-9 8t-9 10l-4 2q-22 22 -56 61t-65 88t-55 106t-24 115v11q0 39 13 57q15 20 39 22t54 -8t61 -19t61 -13t52 10 q32 20 70 57t69 77t51 79t20 65q0 24 8 47t27 37t50 15h3q30 0 72 -19q76 -32 129 -67t83 -57q2 -2 5 -4t5 -4q2 -4 5 -6t5 -4q26 -28 64 -78t78 -120q24 -49 24 -84q0 -26 -11 -44q-13 -20 -35 -30t-48 -10q-24 -2 -61 -25t-75 -58t-71 -75t-51 -74zM-6 0l252 466 q14 -18 28 -38l27 -36q15 -20 31 -36t37 -33t41 -31q22 -16 48 -30z" /> +<glyph unicode="V" horiz-adv-x="1418" d="M638 0q-132 0 -249 51t-204 138t-138 204t-51 249t51 249t138 204t204 138t249 51t249 -51t204 -138t138 -204t51 -249t-51 -249t-138 -204t-204 -138t-249 -51zM196 642q0 -92 35 -172t95 -140t140 -95t172 -35q130 0 238 68l-612 612q-68 -108 -68 -238zM1080 642 q0 92 -34 172t-94 140t-141 95t-173 35q-130 0 -238 -68l612 -612q68 108 68 238z" /> +<glyph unicode="W" horiz-adv-x="1132" d="M846 800q24 0 40 -18t16 -42v-684q0 -24 -16 -41t-40 -17h-788q-24 0 -40 17t-16 41v684q0 24 16 42t40 18h42v124q0 72 28 136t76 112t112 76t136 28q74 0 139 -28t113 -76t76 -112t28 -136v-124h38zM678 800v124q0 46 -18 87t-49 72t-72 49t-87 18t-87 -18t-72 -49 t-49 -72t-18 -87v-124h452z" /> +<glyph unicode="X" horiz-adv-x="1484" d="M-8 58v688q0 26 17 42t43 16h624v124q0 48 -18 89t-49 72t-72 49t-89 18t-90 -18t-74 -50l-114 -12l54 -102q-4 -10 -4 -22v-24v-34h-130v34q0 74 28 139t77 114t114 77t139 28t139 -28t114 -77t77 -114t28 -139v-124h38q22 0 39 -16t17 -42v-688q0 -26 -17 -43t-39 -17 h-792q-26 0 -43 17t-17 43z" /> +<glyph unicode="Y" d="M1138 1282l310 -770v-514h-1446v564l294 720h842zM984 506l294 6l-224 594h-652l-218 -572h282v-284h518v256z" /> +<glyph unicode="Z" d="M1452 516v-516h-1452v566l298 722h844zM1284 516l-228 596h-654l-216 -574h282v-286h518v258zM772 488q-18 -16 -45 -16t-43 16l-184 224v24q0 54 42 54h46q8 0 15 3t7 19v130q0 16 13 29t29 13h152q16 0 29 -13t13 -29v-130q0 -16 6 -19t14 -3h42q56 0 56 -54 q-4 -2 -6 -6q-4 -8 -4 -18z" /> +<glyph unicode="[" horiz-adv-x="1364" d="M1000 626q22 -22 22 -52t-22 -52l-486 -486q-22 -22 -51 -22t-51 22l-370 372q-22 22 -22 52t22 52l486 486q22 22 52 22t52 -22l6 -8l242 244q26 26 62 26t62 -26l238 -238q26 -26 26 -62t-26 -62l-244 -242zM992 914q-12 -14 -12 -32t12 -30l20 -22q30 -30 64 0l20 22 q14 12 14 30t-14 32l-20 22q-34 26 -64 0zM840 1068q-30 -34 0 -66l20 -18q12 -14 31 -14t31 14l22 18q26 32 0 66l-22 18q-12 14 -31 14t-31 -14zM912 542q12 12 12 29t-12 29l-308 310q-12 12 -29 12t-29 -12l-424 -424q-12 -12 -12 -29t12 -29l310 -310q12 -12 29 -12 t29 12z" /> +<glyph unicode="\" horiz-adv-x="1448" d="M638 1286q132 0 248 -51t203 -138t138 -203t51 -248q0 -134 -51 -251t-138 -204t-203 -138t-248 -51q-134 0 -250 51t-203 138t-137 204t-50 251q0 132 50 248t137 203t203 138t250 51zM638 152q102 0 191 39t157 106t107 157t39 192t-39 191t-107 155t-157 105t-191 39 t-192 -39t-157 -105t-106 -155t-39 -191t39 -192t106 -157t157 -106t192 -39zM536 748l380 176l-174 -384l-382 -176zM568 714l-140 -278l282 138z" /> +<glyph unicode="]" horiz-adv-x="1102" d="M774 758q8 -24 32 -41t54 -31l59 -27q29 -13 45 -31t14 -42t-36 -60q-40 -44 -103 -71t-129 -42t-126 -20t-96 -7l-2 -2q-4 -2 -10 -2h-24l-4 2q-32 0 -83 5t-108 18t-113 39t-98 68q-34 34 -37 59t13 42t45 31l58 29q29 15 53 32t30 43q10 36 12 88t-3 103t-17 93 t-30 60t-27 41t-5 46t25 45t67 38q76 28 139 39t101 15q2 0 6 1t6 1q4 -2 8 -1t6 1q38 -4 101 -13t139 -33q46 -16 69 -38t28 -45t-5 -46t-28 -41q-16 -18 -27 -60t-14 -93t0 -103t15 -90zM474 -204l-138 512l46 -8q20 -4 44 -7t46 -7q22 -2 50 1t52 7l56 12z" /> +<glyph unicode="^" horiz-adv-x="1016" d="M730 452q18 -6 25 -22t1 -34q-4 -16 -18 -20l-606 -368q-16 -12 -36 -2q-16 4 -24 20t-2 32q4 6 8 14l200 278l-242 94q-18 6 -25 23t-1 33l8 12q4 6 10 8l348 212l-236 90q-18 6 -25 23t-1 33q8 14 20 22l604 368q20 8 38 2q16 -6 24 -23t0 -33q-4 -2 -4 -5t-2 -5 l-200 -280l240 -94q18 -6 26 -23t0 -33q-2 -14 -18 -22l-344 -208z" /> +<glyph unicode="_" horiz-adv-x="1398" d="M954 82q-46 52 -46 132q0 44 17 83t46 68t68 46t83 17q60 0 112 -34q-44 -102 -116 -181t-164 -131zM640 1282q132 0 249 -51t204 -138t138 -204t51 -249q0 -52 -8 -101t-22 -95q-32 16 -63 25t-67 9q-56 0 -105 -21t-85 -57t-57 -84t-21 -102q0 -46 14 -84t38 -72 q-126 -58 -266 -58q-142 0 -256 56q-18 6 -44 20q-4 2 -6 4t-6 4v-2q-96 52 -168 131t-112 181l4 4q-4 2 -8 10l-7 18q-3 8 -5 18h-2q-30 92 -30 196q0 132 50 249t137 204t204 138t249 51zM272 884q10 20 4 41t-28 31q-16 10 -38 6t-32 -26q-10 -18 -6 -39t26 -33 q18 -10 40 -6t34 26zM640 1206q-22 0 -38 -17t-16 -39t16 -37t38 -15q24 0 38 15t14 37t-14 39t-38 17zM428 1040q18 12 24 34t-6 38q-12 20 -33 26t-41 -4t-23 -32t7 -40q6 -22 28 -27t44 5zM674 510q16 24 40 75t44 105t29 98t-5 52q-12 8 -48 -17t-79 -64t-82 -80 t-53 -65q-32 -46 -32 -94t34 -70q32 -22 77 -4t75 64zM924 1062q12 18 8 40t-26 32q-18 10 -39 4t-33 -26q-10 -16 -6 -38t26 -34q18 -10 39 -5t31 27zM1082 864q20 12 26 33t-4 39q-10 22 -32 26t-40 -6q-22 -10 -28 -31t4 -41q12 -22 33 -26t41 6z" /> +<glyph unicode="`" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" /> +<glyph unicode="a" horiz-adv-x="1144" d="M904 970l-82 -930q0 -16 -12 -28t-28 -12h-574q-16 0 -28 12t-12 28l-82 930h818zM966 1136q22 -14 22 -30v-54q0 -22 -22 -22h-334h-280h-334q-20 0 -20 22v54q0 16 20 30q20 6 38 14q16 6 31 13t21 13q4 6 11 20t13 30q8 18 12 38q4 6 9 15t19 9h180h280h180 q14 0 19 -9t9 -15q4 -20 12 -38q6 -16 13 -30t13 -20t20 -13t30 -13q18 -8 38 -14zM622 1158q0 20 -20 20h-218q-20 0 -20 -20v-14q0 -22 20 -22h218q20 0 20 22v14z" /> +<glyph unicode="b" horiz-adv-x="1200" d="M142 694q0 14 14 14h4q8 0 14 -6l34 -58l38 58q2 6 12 6h2q6 0 12 -4t6 -10v-104q0 -6 -6 -12t-12 -6q-4 0 -9 6t-5 12v66l-24 -40q-8 -8 -14 -8t-12 8l-26 38v-66q0 -4 -4 -10t-10 -6t-10 6t-4 10v106zM286 592l46 108q8 12 18 12h2q14 0 18 -12l46 -108q0 -2 2 -2l2 -2 q0 -4 -6 -10t-10 -6q-6 0 -8 4t-6 8l-8 20h-62l-8 -20q-4 -12 -14 -12q-4 0 -8 6t-4 10v4zM370 630l-20 46l-18 -46h38zM428 694q0 14 14 14h46q28 0 38 -12q12 -12 12 -30q0 -34 -26 -42l20 -26q6 -6 6 -10q0 -16 -14 -16q-8 0 -10 3t-6 7l-28 36h-24v-28q0 -6 -3 -12 t-11 -6q-6 0 -10 6t-4 12v104zM486 644q22 0 22 18q0 20 -22 20h-30v-38h30zM514 436l-28 -8h-14q-14 0 -25 9t-11 23q0 30 26 34l52 14q12 4 20 7t18 3q18 0 29 -12t11 -28v-292q0 -18 -11 -30t-29 -12q-16 0 -27 12t-11 30v250zM682 206l136 236h-138q-16 0 -26 10t-10 26 t10 25t26 9h196q34 0 34 -34q0 -22 -10 -42l-146 -262q-16 -30 -36 -30q-18 0 -31 11t-13 27q0 4 3 12t5 12zM362 1026q0 -40 -27 -67t-67 -27t-66 27t-26 67v160q0 40 26 68t66 28t67 -28t27 -68v-160zM916 1026q0 -40 -26 -67t-66 -27t-68 27t-28 67v160q0 40 28 68t68 28 t66 -28t26 -68v-160zM1018 1094h-34v-90q0 -34 -13 -63t-35 -50t-51 -34t-61 -13q-68 0 -113 47t-45 113v90h-10h-226h-2v-90q0 -66 -47 -113t-113 -47q-64 0 -111 47t-47 113v90h-32q-34 0 -56 -23t-22 -55v-938q0 -34 22 -56t56 -22h940q30 0 53 22t23 56v938q0 32 -23 55 t-53 23zM994 170q0 -24 -18 -41t-42 -17h-772q-24 0 -42 17t-18 41v532q0 24 18 41t42 17h772q24 0 42 -17t18 -41v-532z" /> +<glyph unicode="c" horiz-adv-x="1466" d="M544 644q0 -18 1 -29t5 -20t14 -19l28 -28q36 -42 92 -44h7q52 0 93 26q6 0 12 6t6 12l230 222l58 58q44 42 44 104t-44 106t-106 44t-104 -44l-20 -18q-52 22 -106 23h-6q-51 0 -100 -15l144 144q44 44 99 65t113 21t113 -21t99 -65l8 -8q44 -44 65 -98t21 -109 t-21 -109t-65 -98l-192 -192l-144 -144q-58 -58 -138 -78q-38 -10 -76 -10q-41 0 -82 12q-30 8 -64 28t-62 48v10q-28 28 -36 44t-22 52zM792 626q0 36 -6 53t-42 53t-92 41q-8 1 -17 1q-46 0 -83 -24q-6 -8 -12 -10t-6 -8l-230 -222l-58 -66q-44 -44 -44 -106t44 -106 t106 -44t104 44l20 20q52 -16 106 -19h13q47 0 93 19l-144 -154q-44 -40 -99 -60t-113 -20t-113 20t-99 60l-8 18q-44 40 -66 93t-22 110t22 112t66 99l192 192l144 144q58 58 138 73q32 6 65 6q47 0 93 -13q30 -6 64 -25t62 -41v-10q14 -14 22 -26t14 -24t10 -25t12 -31z " /> +<glyph unicode="d" horiz-adv-x="1072" d="M906 778v-676q0 -42 -30 -72t-72 -30h-638q-44 0 -74 30t-30 72v676h844zM336 626q0 46 -44 46q-40 0 -40 -46v-464q0 -18 11 -32t29 -14q20 0 32 14t12 32v464zM534 626q0 46 -44 46q-16 0 -30 -12t-14 -34v-464q0 -18 14 -32t30 -14q20 0 32 14t12 32v464zM718 626 q0 22 -14 34t-30 12q-44 0 -44 -46v-464q0 -18 12 -32t32 -14q16 0 30 14t14 32v464zM968 864h-966v162q0 24 17 43t43 19h162v162q0 14 9 24t23 10h440q16 0 25 -10t9 -24v-162h178q24 0 42 -19t18 -43v-162zM642 1152q0 14 -10 23t-24 9h-262q-14 0 -24 -9t-10 -23v-64 h330v64z" /> +<glyph unicode="e" horiz-adv-x="1430" d="M264 180q56 4 93 35q33 28 33 76v11q-4 54 -45 79q-35 22 -80 22q-7 0 -15 -1q-54 -4 -91 -34q-33 -27 -33 -75v-11q4 -54 44 -80q35 -23 80 -23q7 0 14 1zM228 754q54 4 90 35q32 28 32 74v11q-4 56 -44 80q-34 21 -79 21q-7 0 -15 -1q-56 -4 -93 -33q-33 -26 -33 -74 v-11q4 -56 45 -81q35 -22 82 -22q7 0 15 1zM204 1058q10 1 20 1q77 0 136 -42q66 -47 76 -137q2 -46 -16 -78t-46 -64l142 -66l512 348q44 25 89 25l31 -3q60 -12 124 -50l-612 -388l658 -302q-58 -46 -116 -65q-26 -8 -52 -8q-33 0 -68 13l-556 276l-130 -84q34 -26 53 -53 t23 -73q1 -11 1 -21q0 -77 -53 -126q-60 -55 -148 -61q-13 -1 -26 -1q-75 0 -132 40q-66 47 -72 137q-1 8 -1 17q0 79 53 128q58 55 146 61l150 104l-170 86q-86 0 -149 46t-67 132q-1 8 -1 17q0 79 53 129q58 56 148 62z" /> +<glyph unicode="f" horiz-adv-x="1258" d="M966 1288q46 0 77 -31t31 -77v-1070q0 -46 -31 -77t-77 -31h-856q-46 0 -77 31t-31 77v1070q0 46 31 77t77 31h856zM862 592h-218v374h-212v-374h-214l322 -320z" /> +<glyph unicode="g" horiz-adv-x="864" d="M686 1084v-738q0 -44 -15 -104t-54 -114t-105 -91t-168 -37t-169 37t-106 91t-55 114t-16 104v688q0 4 4 40t28 80q62 126 214 126q76 0 131 -32t83 -94q24 -44 28 -80t4 -40v-638q0 -22 -8 -49t-25 -49t-45 -37t-68 -15q-42 0 -70 15t-46 37t-26 49t-8 49v442 q0 22 14 36t36 14t36 -14t14 -36v-442q0 -16 10 -33t40 -17q28 0 37 17t9 33v638q0 8 -3 33t-17 50t-44 45t-82 20q-88 0 -128 -70q-8 -16 -11 -30l-5 -24q-2 -12 -2 -24v-688q0 -10 5 -50t28 -83t73 -78t140 -35t140 37t74 82t28 84t4 43v738q0 22 14 36t32 14q22 0 36 -14 t14 -36z" /> +<glyph unicode="h" horiz-adv-x="1460" d="M342 170q0 -68 -50 -118t-122 -50q-36 0 -68 14t-55 37t-37 53t-14 64q0 72 51 123t123 51t122 -51t50 -123zM840 12h-228q0 120 -46 229t-132 195q-86 90 -195 136t-229 46v228q170 -4 321 -71t263 -179t178 -263t68 -321zM1282 12h-228q-2 216 -84 406t-224 332 t-331 225t-405 85v224q174 -2 336 -48t302 -130t256 -200t199 -257t130 -302t49 -335z" /> +<glyph unicode="i" horiz-adv-x="1412" d="M644 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM766 814q0 62 -70 62q-32 0 -84 -34q-28 -20 -54 -42t-54 -54q-52 -58 -52 -70v-16 q2 -4 8 -5t10 -1q8 0 36 36q76 90 106 90q10 0 10 -16q0 -10 -5 -27t-19 -43l-162 -326q-14 -34 -22 -60t-8 -46q0 -64 72 -64q36 0 84 30q24 22 51 44t57 50q28 30 40 50t12 24q0 12 -2 16q-8 10 -16 10q-4 0 -46 -44q-78 -88 -96 -88q-10 0 -10 18q0 10 5 27t19 45 l162 326q28 62 28 108zM854 1022q0 34 -28 62q-24 24 -58 24q-16 0 -32 -6t-26 -18q-24 -24 -24 -62q0 -32 24 -56q10 -12 26 -17t32 -5q36 0 58 22q28 28 28 56z" /> +<glyph unicode="j" d="M288 1038v236h804l6 -236h-810zM1334 198h-162v372h-958v-372h-156q-28 0 -45 19t-17 43v640q0 28 17 49t45 21h1276q28 0 45 -21t17 -49v-640q0 -24 -17 -43t-45 -19zM444 4l-150 150v342h804v-492h-654z" /> +<glyph unicode="k" horiz-adv-x="1072" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41z" /> +<glyph unicode="l" horiz-adv-x="1112" d="M1002 646v-630q0 -18 -14 -30t-32 -12h-598q-20 0 -33 12t-13 30v884q0 16 13 29t33 13h344zM272 1034q-22 0 -38 -17t-16 -41v-372h-182q-14 0 -23 10t-9 24v614q0 10 9 19t23 9h240l208 -208v-38h-212z" /> +<glyph unicode="m" horiz-adv-x="1126" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41zM448 148q74 0 141 28t116 78t78 115t29 141q0 74 -29 141t-78 116t-116 78t-141 29q-76 0 -141 -29t-115 -78t-78 -116t-28 -141q0 -76 28 -141t78 -115t115 -78 t141 -28zM198 510q0 38 11 71t29 63l344 -344q-30 -18 -63 -29t-71 -11q-52 0 -97 20t-79 54t-54 79t-20 97zM698 510q0 -76 -38 -134l-346 346q58 38 134 38q52 0 98 -20t80 -54t53 -79t19 -97z" /> +<glyph unicode="n" d="M650 206l-276 276l276 278q12 10 12 28t-12 28l-136 138q-15 13 -30 13l-30 -13l-296 -298q-4 0 -5 -2t-5 -4l-138 -136q-14 -14 -10 -32q-4 -16 10 -30l138 -136q6 -6 10 -6l296 -300q15 -12 30 -12l30 12l136 140q12 10 12 28t-12 28zM808 206l276 276l-276 278 q-10 10 -10 28t10 28l138 138q14 14 28.5 14t29.5 -14l296 -298q4 0 5 -2t5 -4l138 -136q14 -14 10 -32q4 -16 -10 -30l-138 -136q-6 -6 -10 -6l-296 -300q-15 -12 -30 -12l-30 12l-136 140q-10 10 -10 28t10 28z" /> +<glyph unicode="o" horiz-adv-x="1340" d="M1098 1280q74 0 126 -52t52 -126q0 -76 -50 -126v-2l-154 -156q20 -18 29 -45t9 -56t-9 -56t-29 -47l-154 -154l-104 102l-510 -514l-208 -48q-40 0 -70 30t-30 70l48 208l512 514l-100 100l154 154q20 20 47 29t56 9t56 -9t45 -29l156 154q26 26 59 38t69 12z" /> +<glyph unicode="p" horiz-adv-x="1466" d="M802 1238l40 -79q22 -45 47 -87t51 -76t50 -42q14 8 24 18q4 60 44 98t90 44q8 1 16 1q41 0 81 -26q47 -31 63 -109q6 -32 6 -55q0 -16 -3 -29q-7 -30 -26 -47t-47 -23t-59 -10t-60 -10t-51 -22q34 -76 73 -152l75 -148q-26 -16 -60 -39l-70 -46q-36 -23 -73 -42t-69 -25 q-10 4 -12 14t-6 14q4 26 4 50q0 39 -10 73q-16 55 -52 80q-24 16 -55 16q-16 0 -34 -4q-53 -13 -117 -81q-36 -38 -39 -69t13 -58t44 -48l54 -39l45 -31q19 -13 15 -23l-129 -77q-63 -37 -127 -79l-91 142l-91 142q-6 -4 -16 -7t-18 -5q-14 -48 -51 -92t-83 -59 q-17 -6 -34 -6q-29 0 -57 16q-45 25 -75 113q-14 42 -14 72q0 21 7 37q17 37 54 55t85 22t88 8q4 2 9 6t9 8l-80 150l-80 148l136 81l136 83q16 -20 21 -46t6 -54l2 -56q1 -28 11 -49t34 -34q21 -11 58 -11h12q44 0 84 26t64 65q26 42 26 90q0 42 -28 79q-18 10 -38 16 l-38 11q-18 5 -31 11t-17 18q-10 26 -3 26t7 8l132 77z" /> +<glyph unicode="q" horiz-adv-x="1620" d="M744 1280q154 0 290 -14t237 -38t159 -57t58 -71v-4q0 -44 -36 -94t-91 -103t-123 -109l-131 -109q-63 -53 -114 -104t-73 -95v-392q0 -38 -52 -63t-124 -25t-124 25t-52 63v392q-22 44 -72 95t-113 104l-131 109q-68 56 -124 109t-92 103t-36 94v4q0 38 58 71t159 57 t237 38t290 14zM744 1204q-118 0 -222 -8t-182 -22t-123 -32t-45 -38q0 -22 45 -39t123 -31t182 -21t222 -7t222 7t182 21t123 31t45 39q0 20 -45 38t-123 32t-182 22t-222 8z" /> +<glyph unicode="r" horiz-adv-x="1442" d="M1254 184q30 -32 30 -76t-30 -76q-16 -16 -37 -24t-39 -8q-46 0 -78 32l-308 308q-2 4 -2 6t-4 6q-62 -40 -132 -62t-146 -22q-106 0 -198 40t-160 109t-108 162t-40 197q0 106 40 199t108 162t160 109t198 40t198 -40t161 -109t109 -162t40 -199q0 -76 -21 -146 t-59 -128q4 -4 5 -6t5 -6zM508 428q72 0 136 28t111 75t74 110t27 135t-27 135t-74 110t-111 75t-136 28t-135 -28t-111 -75t-75 -110t-27 -135t27 -135t75 -110t111 -75t135 -28z" /> +<glyph unicode="s" horiz-adv-x="1152" d="M672 840q102 -150 182 -282l67 -116q33 -58 59 -112t42 -98t16 -74q0 -58 -25 -89t-53 -47q-36 -18 -78 -20h-368h-364q-44 2 -78 20q-30 16 -54 47t-24 89q0 30 15 74t41 98t59 112l67 116q80 132 184 282v346h-22q-20 0 -34 13t-14 35q0 18 14 31t34 13h358 q20 0 33 -13t13 -31q0 -22 -13 -35t-33 -13h-24v-346z" /> +<glyph unicode="t" horiz-adv-x="1466" d="M1308 1022q30 0 52 -23t22 -53v-876q0 -32 -22 -52t-52 -20h-1232q-32 0 -54 20t-22 52v876q0 30 22 53t54 23h226v218q0 16 12 29t28 13h698q18 0 30 -13t12 -29v-218h226zM412 1022h560v96q0 16 -13 28t-29 12h-478q-16 0 -28 -12t-12 -28v-96z" /> +<glyph unicode="u" d="M1360 234h-984q-28 0 -47 20t-19 46l-82 614l-204 176q-24 18 -24 48q0 28 21 47t45 19q26 0 44 -14h4l200 -172l1152 -94q28 0 49 -19t21 -43l-108 -562q0 -26 -20 -46t-48 -20zM1332 0q-36 0 -63 28q-24 24 -24 58q0 5 1 10q0 34 28 59t62 25q36 -4 61 -29t25 -65 q0 -36 -27 -61t-63 -25zM386 0q-36 0 -64 27t-28 63t28 63t64 27q34 0 62 -27t28 -63t-28 -63t-62 -27z" /> +<glyph unicode="v" horiz-adv-x="1346" d="M762 538v-300l160 -128v-108l-268 108l-268 -108v108l160 128v300l-482 -162v108l482 322v374q2 30 14 54q10 20 32 37t62 17t62 -17t32 -37q12 -24 14 -54v-374l480 -322v-108z" /> +<glyph unicode="w" horiz-adv-x="1172" d="M1004 984q-2 -2 -2 -6v-148q2 -6 2 -20q-2 -2 -2 -4v-2v-2q-6 -42 -45 -81t-113 -71q-76 -34 -169 -50t-191 -16h-21q-85 0 -166 14q-91 16 -163 50q-72 32 -107 73q-31 37 -31 74q0 4 1 8.5t1 8.5v146q-2 6 -2 20q2 6 2 18q18 -76 136 -128q72 -34 163 -49t187 -15 q98 0 191 17t169 49q70 30 108 67t48 77q4 -20 4 -30zM1004 674q-2 0 -2 -4v-150q2 -4 2 -18q-2 -2 -2 -8v-2q-6 -40 -45 -79t-113 -71q-76 -34 -169 -51t-191 -17h-20q-85 0 -167 15q-91 17 -163 49q-72 34 -107 75q-31 37 -31 74q0 4 1 8.5t1 8.5v144q-2 8 -2 22q2 4 2 8 v8q18 -72 136 -128q72 -32 163 -48t187 -16q98 2 191 18t169 50q70 30 108 67t48 77q2 -8 3 -15t1 -17zM1004 356q-2 -2 -2 -8v-146q2 -8 2 -20q-2 -2 -2 -6v-4q-6 -40 -45 -79t-113 -69q-76 -34 -169 -51t-191 -19q-96 0 -187 15t-163 49q-138 63 -138 153q0 3 1 7t1 6v146 q-2 6 -2 20q2 6 2 18q10 -34 43 -68t93 -60q72 -34 163 -49t187 -15q98 0 191 17t169 51q70 30 108 67t48 75q4 -20 4 -30zM134 962q-118 56 -136 128q10 40 49 77t107 67q76 34 170 50t192 18q96 0 188 -15t162 -49q118 -54 134 -128q-20 -82 -156 -142q-76 -34 -169 -51 t-191 -17h-21q-85 0 -166 14q-91 16 -163 48z" /> +<glyph unicode="x" horiz-adv-x="1358" d="M794 364h11q101 0 171 -36q74 -38 126 -96t86 -124l60 -116h-628h-628l60 116q34 66 85 124t124 96q69 36 168 36h11q34 0 54 23t20 55q0 10 -15 29t-31 37q-20 22 -44 46q-36 38 -64 85t-46 98t-27 102t-9 97q0 70 26 132t72 109t108 74t134 27h2q70 0 132 -27t108 -74 t73 -109t27 -132q0 -46 -9 -97t-27 -102t-45 -98t-63 -85q-26 -24 -46 -46q-16 -18 -30 -37t-14 -29q0 -32 20 -55t48 -23z" /> +<glyph unicode="y" horiz-adv-x="1298" d="M630 844v-210h426q14 0 14 -12v-288q0 -12 -14 -12h-426v-304q0 -20 -20 -20h-102q-20 0 -20 20v304h-300v-2q-18 -18 -36 2l-140 136q-10 10 -10 21t8 17l142 144q8 4 18 4t18 -8v-2h300v210h-384q-14 0 -14 12v288q0 12 14 12h384v108q0 18 20 18h102q20 0 20 -18v-108 h340v4q22 18 40 0l138 -142q9 -7 9 -16q0 -10 -9 -22l-138 -138q-8 -8 -19 -8t-19 8v2h-342z" /> +<glyph unicode="z" horiz-adv-x="1072" d="M724 942l142 -228q-74 6 -152 14q-66 6 -143 11t-147 5q-102 0 -175 -11t-107 -17v-694q0 -20 -20 -20h-100q-20 0 -20 20v1246q0 20 20 20h100q20 0 20 -20v-88q34 8 107 20t175 12q70 0 147 -6t143 -12l152 -16z" /> +<glyph unicode="{" horiz-adv-x="638" d="M452 8l-226 226l-226 -226v1268h452v-1268z" /> +<glyph unicode="|" horiz-adv-x="822" d="M540 1266q36 -10 58 -38t24 -64q38 8 71 -6t53 -48q18 -34 13 -70t-31 -62q32 -22 46 -55t4 -69q-10 -38 -38 -60t-66 -24q10 -34 -4 -69t-48 -53q-34 -20 -69 -15t-63 29q-22 -30 -55 -45t-69 -5t-59 40t-25 66q-34 -8 -69 6t-53 48q-20 32 -15 69t29 63q-30 20 -44 53 t-4 73q10 36 39 57t65 23q-8 36 6 70t48 54q32 20 69 14t63 -30q20 32 54 45t70 3zM492 1086q-60 16 -114 -15t-70 -91q-16 -62 15 -115t93 -69q30 -8 59 -3t54 19t43 37t26 53q16 60 -15 114t-91 70zM254 610q2 -36 24 -66t60 -40q36 -10 68 5t54 45q16 -16 35 -22t39 -8 l-144 -538l-80 139q-28 49 -59 60t-80 -16t-143 -87l144 540q42 -22 82 -12z" /> +<glyph unicode="}" horiz-adv-x="1406" d="M662 -52q-134 0 -250 56l250 532l92 -226l37 -92l36 -88l32 -78q15 -36 25 -58q-104 -46 -222 -46zM338 120q-74 36 -136 90t-106 123t-69 150t-25 169q0 70 17 136t47 124l522 -260zM932 188q-10 22 -24 57l-31 77l-36 88l-37 92l-92 224l-522 264q36 72 90 131t121 102 t146 67t165 24q120 0 227 -46t186 -126t125 -187t46 -229q0 -92 -27 -175t-75 -153t-115 -124t-147 -86z" /> +<glyph unicode="~" d="M1328 686q32 -34 34 -80t-30 -74h-8v-484q0 -22 -16 -38t-38 -16h-238v454q0 32 -23 55t-53 23h-224q-32 0 -54 -23t-22 -55v-454h-546q-20 0 -37 16t-17 38v478h-8q-32 34 -34 79t30 73l568 572q34 28 79 27t75 -35z" /> +<glyph unicode="­" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" /> +<glyph unicode="É" horiz-adv-x="1502" /> +<glyph unicode="Ñ" horiz-adv-x="1030" d="M718 1178q-22 -116 4 -224t61 -212t59 -205t-4 -203q-22 -80 -73 -142t-118 -99t-147 -47t-162 12t-144 72t-101 117t-50 145t11 158q18 68 58 124t90 105t107 95t110 97t98 109t73 132l4 14q8 28 31 41t51 5q26 -8 39 -31t7 -49zM418 744q26 4 44 24t16 48q-4 28 -25 46 t-49 14t-45 -24t-15 -48t24 -45t50 -15zM306 594q16 12 20 34t-8 40q-16 22 -39 28t-47 -10q-28 -16 -50 -56t-36 -90q-12 -52 -9 -102t19 -74q14 -24 37 -29t45 7q16 10 24 32t0 40q-16 46 -5 95t49 85z" /> +<glyph unicode="á" horiz-adv-x="1394" d="M252 152q18 22 18 50t-18 50q-22 18 -52 18t-48 -18q-22 -22 -22 -50t22 -50q18 -22 48 -22t52 22zM64 294l18 18l500 500q-32 92 -10 187t92 165q62 62 141 88t159 12l-200 -200l66 -234l234 -66l200 200q10 -80 -14 -158t-86 -136q-70 -76 -167 -98t-185 10l-500 -500 l-18 -18q-48 -48 -114 -48t-116 48q-48 50 -48 118t48 112z" /> +<glyph unicode="é" horiz-adv-x="1376" d="M626 1022q-80 0 -149 -30t-120 -81t-81 -120t-30 -149q0 -82 30 -152t81 -121t120 -81t149 -30t151 30t122 81t81 121t30 152q0 80 -30 149t-81 120t-122 81t-151 30zM198 580l-168 14q-10 0 -20 12t-10 36q0 22 10 33t20 11l168 16v-122zM1054 702l170 -16q46 0 46 -44 q0 -14 -12 -31t-34 -17l-170 -14q14 22 14 61t-14 61zM688 214l-14 -170q0 -12 -10 -29t-20 -17q-24 0 -36 17t-12 29l-30 170q12 -6 30 -10t37 -4t34 4t21 10zM920 1008l104 92q0 12 17 12t31 -12q10 -12 10 -32t-10 -30l-92 -106q-12 22 -31 43t-29 33zM368 304l-108 -90 q-22 -28 -44 0q-14 10 -14 29t14 31l74 108q26 -56 78 -78zM994 382l78 -108q20 -24 0 -46q0 -10 -18 -10t-30 10l-104 90q22 0 43 26zM290 932l-92 106q-24 22 0 46q26 22 48 0l104 -92q-6 0 -16 -7t-20 -17t-17 -20t-7 -16zM566 1070l16 168q0 22 17 34t27 12q48 0 48 -46 l14 -168q-22 10 -61 10t-61 -10z" /> +<glyph unicode="í" horiz-adv-x="1400" d="M638 1272q134 0 251 -49t203 -135t135 -203t49 -251t-49 -250t-135 -202t-203 -135t-251 -49t-250 49t-202 135t-135 202t-49 250t49 251t135 203t202 135t250 49zM638 100q114 0 212 41t170 112t113 169t41 212t-41 212t-113 170t-170 113t-212 41v-1070z" /> +<glyph unicode="ñ" d="M1222 972q22 0 37 -17t15 -39v-232q0 -22 -15 -37t-37 -15h-948q-22 0 -37 15t-15 37v232q0 22 15 39t37 17h948zM408 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM882 740q-6 14 -8 29t-2 31q0 40 10 62h-268l8 -29 q4 -15 4 -33q0 -16 -4 -31l-8 -29h268zM1088 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM1362 1172q52 0 87 -36t35 -86v-868q0 -52 -35 -87t-87 -35h-1236q-52 0 -88 35t-36 87v868q0 50 36 86t88 36h1236zM240 130q18 0 30 13 t12 31t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM368 242q18 0 30 12t12 30q0 16 -12 28t-30 12t-30 -12t-12 -28q0 -18 12 -30t30 -12zM1140 242q16 0 28 12t12 30q0 16 -12 28t-28 12q-18 0 -30 -12t-12 -28q0 -18 12 -30t30 -12zM1266 130q18 0 30 13t12 31 t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM1396 1020q0 22 -15 39t-39 17h-1190q-22 0 -37 -17t-15 -39v-568q0 -22 15 -37t37 -15h1190q24 0 39 15t15 37v568z" /> +<glyph unicode="ó" horiz-adv-x="1418" d="M304 1284q30 0 53 -22t23 -52v-226q0 -32 -23 -54t-53 -22h-226q-32 0 -55 22t-23 54v226q0 30 23 52t55 22h226zM756 1284q32 0 53 -22t21 -52v-226q0 -32 -21 -54t-53 -22h-228q-30 0 -52 22t-22 54v226q0 30 22 52t52 22h228zM1210 1284q32 0 53 -22t21 -52v-226 q0 -32 -21 -54t-53 -22h-226q-32 0 -54 22t-22 54v226q0 30 22 52t54 22h226zM304 830q30 0 53 -21t23 -53v-228q0 -30 -23 -52t-53 -22h-226q-32 0 -55 22t-23 52v228q0 32 23 53t55 21h226zM756 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-228q-30 0 -52 22 t-22 52v228q0 32 22 53t52 21h228zM1210 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-226q-32 0 -54 22t-22 52v228q0 32 22 53t54 21h226zM304 380q30 0 53 -23t23 -55v-228q0 -28 -23 -51t-53 -23h-226q-32 0 -55 23t-23 51v228q0 32 23 55t55 23h226zM756 380 q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-228q-30 0 -52 23t-22 51v228q0 32 22 55t52 23h228zM1210 380q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-226q-32 0 -54 23t-22 51v228q0 32 22 55t54 23h226z" /> +<glyph unicode="ö" horiz-adv-x="1412" d="M1214 1282q32 0 53 -22t21 -54v-226q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v226q0 32 21 54t53 22h906h112h114zM1214 828q32 0 53 -21t21 -51v-228q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v228q0 30 21 51t53 21h906h112h114z M1214 378q32 0 53 -23t21 -55v-224q0 -30 -21 -53t-53 -23h-114h-112h-906q-32 0 -53 23t-21 53v224q0 32 21 55t53 23h906h112h114z" /> +<glyph unicode="ú" horiz-adv-x="1370" d="M1248 698q24 -24 24 -57t-24 -57l-552 -552q-26 -26 -58 -26t-58 26l-554 552q-22 24 -22 57t22 57l554 552q26 24 58 24t58 -24zM720 1104q10 8 10 21t-10 23l-62 60q-22 20 -42 0l-60 -60q-10 -10 -10 -23t10 -21l60 -60q10 -10 21 -10t21 10zM558 944q20 20 0 42 l-58 60q-24 24 -44 0l-58 -60q-12 -10 -12 -21t12 -21l58 -62q20 -20 44 0zM404 786q22 22 0 46l-62 56q-22 20 -42 0l-60 -56q-22 -24 0 -46l60 -58q10 -10 21 -10t21 10zM240 624q20 20 0 42l-58 62q-10 10 -23 10t-23 -10l-58 -62q-12 -10 -12 -21t12 -21l58 -60 q10 -10 23 -10t23 10zM1196 624q10 10 10 23t-10 23l-56 58q-10 10 -23 10t-23 -10l-58 -58q-12 -10 -12 -23t12 -23l58 -58q10 -10 23 -10t23 10zM1036 466q10 8 10 20t-10 22l-58 62q-10 6 -23 6t-23 -6l-58 -62q-10 -10 -10 -22t10 -20l58 -60q10 -10 23 -10t23 10z M880 308q10 10 10 23t-10 23l-60 58q-22 20 -42 0l-62 -58q-10 -10 -10 -23t10 -23l62 -58q10 -10 21 -10t21 10zM716 146q20 24 0 44l-58 60q-10 10 -22 10t-22 -10l-58 -60q-20 -20 0 -44l58 -60q10 -10 22 -10t22 10zM738 442q10 10 10 23t-10 23l-258 258q-10 10 -23 10 t-23 -10l-226 -226q-20 -20 0 -44l260 -260q10 -10 22 -10t22 10zM1060 764q8 10 8 23t-8 19l-262 262q-22 20 -42 0l-224 -224q-10 -10 -10 -23t10 -23l258 -258q10 -10 23 -10t23 10z" /> +<glyph unicode=" " horiz-adv-x="886" /> +<glyph unicode=" " horiz-adv-x="1774" /> +<glyph unicode=" " horiz-adv-x="886" /> +<glyph unicode=" " horiz-adv-x="1774" /> +<glyph unicode=" " horiz-adv-x="590" /> +<glyph unicode=" " horiz-adv-x="442" /> +<glyph unicode=" " horiz-adv-x="294" /> +<glyph unicode=" " horiz-adv-x="294" /> +<glyph unicode=" " horiz-adv-x="220" /> +<glyph unicode=" " horiz-adv-x="354" /> +<glyph unicode=" " horiz-adv-x="98" /> +<glyph unicode="‐" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" /> +<glyph unicode="‑" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" /> +<glyph unicode="‒" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" /> +<glyph unicode="–" horiz-adv-x="1094" d="M842 1288q38 0 71 -18t58 -50t39 -75t14 -93v-822q0 -48 -14 -91t-39 -75t-58 -51t-71 -19h-655q-38 0 -72 19t-59.5 51t-40.5 75t-15 91v822q0 50 15 93t40.5 75t59.5 50t72 18h655zM842 756h-234h-421v-230h421h234v230z" /> +<glyph unicode="—" horiz-adv-x="2118" d="M1684 1288q76 0 142.5 -18t115.5 -50t77.5 -75t28.5 -93v-822q0 -48 -28.5 -91t-77.5 -75t-115.5 -51t-142.5 -19h-1310q-76 0 -144.5 19t-119 51t-80.5 75t-30 91v822q0 50 30 93t80.5 75t119 50t144.5 18h1310zM1684 756h-468h-842v-230h842h468v230z" /> +<glyph unicode="‘" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" /> +<glyph unicode="’" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" /> +<glyph unicode="“" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" /> +<glyph unicode="”" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" /> +<glyph unicode=" " horiz-adv-x="354" /> +<glyph unicode=" " horiz-adv-x="442" /> +<glyph unicode="" horiz-adv-x="1280" d="M0 1280h1280v-1280h-1280v1280z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.ttf b/addons/web/static/src/font/mnmliconsv21-webfont.ttf Binary files differnew file mode 100755 index 00000000000..c38e59cf138 --- /dev/null +++ b/addons/web/static/src/font/mnmliconsv21-webfont.ttf diff --git a/addons/web/static/src/font/mnmliconsv21-webfont.woff b/addons/web/static/src/font/mnmliconsv21-webfont.woff Binary files differnew file mode 100755 index 00000000000..3ddf14fb814 --- /dev/null +++ b/addons/web/static/src/font/mnmliconsv21-webfont.woff diff --git a/addons/web/static/src/img/form_sheetbg.png b/addons/web/static/src/img/form_sheetbg.png Binary files differnew file mode 100644 index 00000000000..d54b6302d5a --- /dev/null +++ b/addons/web/static/src/img/form_sheetbg.png diff --git a/addons/web/static/src/img/form_steps.png b/addons/web/static/src/img/form_steps.png Binary files differnew file mode 100644 index 00000000000..0485c5977aa --- /dev/null +++ b/addons/web/static/src/img/form_steps.png diff --git a/addons/web/static/src/img/list_empty_arrow.png b/addons/web/static/src/img/list_empty_arrow.png Binary files differnew file mode 100644 index 00000000000..b0b1372adfd --- /dev/null +++ b/addons/web/static/src/img/list_empty_arrow.png diff --git a/addons/web/static/src/img/topbar-avatar.png b/addons/web/static/src/img/topbar-avatar.png Binary files differdeleted file mode 100644 index a2c70ea7d49..00000000000 --- a/addons/web/static/src/img/topbar-avatar.png +++ /dev/null diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js index 282435f2a71..1251975f41c 100644 --- a/addons/web/static/src/js/boot.js +++ b/addons/web/static/src/js/boot.js @@ -47,26 +47,15 @@ /*--------------------------------------------------------- * OpenERP Web web module split *---------------------------------------------------------*/ - -/** - * @namespace - */ -openerp.web = function(instance) { - openerp.web.core(instance); - if (openerp.web.dates) { - openerp.web.dates(instance); - } - openerp.web.formats(instance); - openerp.web.chrome(instance); - openerp.web.data(instance); - var files = ["views","search","list","form", "page","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"]; +openerp.web = function(session) { + var files = ["corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"]; for(var i=0; i<files.length; i++) { if(openerp.web[files[i]]) { - openerp.web[files[i]](instance); + openerp.web[files[i]](session); } } - instance.log = function() { - if (instance.connection.debug && window.console) { + session.log = function() { + if (session.connection.debug && window.console) { console.log.apply(console, arguments); } } diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 7743e2b5777..f1d3faef367 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1,18 +1,17 @@ /*--------------------------------------------------------- * OpenERP Web chrome *---------------------------------------------------------*/ -openerp.web.chrome = function(openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t; +openerp.web.chrome = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; -openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.Notification# */{ +instance.web.Notification = instance.web.Widget.extend({ template: 'Notification', - init: function() { this._super.apply(this, arguments); - openerp.notification = this; + // move to instance.web.notification + instance.notification = this; }, - start: function() { this._super.apply(this, arguments); this.$element.notify({ @@ -42,24 +41,16 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web. text: text }, opts); } - }); -openerp.web.dialog = function(element) { +instance.web.dialog = function(element) { var result = element.dialog.apply(element, _.rest(_.toArray(arguments))); result.dialog("widget").addClass("openerp"); return result; } -openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{ +instance.web.Dialog = instance.web.Widget.extend({ dialog_title: "", - /** - * @constructs openerp.web.Dialog - * @extends openerp.web.OldWidget - * - * @param parent - * @param options - */ init: function (parent, options, content) { var self = this; this._super(parent); @@ -92,7 +83,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# if (this.dialog_options.autoOpen) { this.open(); } else { - openerp.web.dialog(this.$element, this.get_options()); + instance.web.dialog(this.$element, this.get_options()); } }, get_options: function(options) { @@ -128,10 +119,10 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# open: function(options) { // TODO fme: bind window on resize if (this.template) { - this.$element.html(this.render()); + this.$element.html(this.renderElement()); } var o = this.get_options(options); - openerp.web.dialog(this.$element, o).dialog('open'); + instance.web.dialog(this.$element, o).dialog('open'); if (o.height === 'auto' && o.max_height) { this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' }); } @@ -149,14 +140,13 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# //openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height()); }, destroy: function () { - // Destroy widget this.close(); this.$element.dialog('destroy'); this._super(); } }); -openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ +instance.web.CrashManager = instance.web.CallbackEnabled.extend({ on_rpc_error: function(error) { if (error.data.fault_code) { var split = ("" + error.data.fault_code).split('\n')[0].split(' -- '); @@ -172,7 +162,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ } }, on_managed_error: function(error) { - openerp.web.dialog($('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>'), { + instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), { title: "OpenERP " + _.str.capitalize(error.type), buttons: [ {text: _t("Ok"), click: function() { $(this).dialog("close"); }} @@ -182,14 +172,14 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ on_traceback: function(error) { var self = this; var buttons = {}; - if (openerp.connection.openerp_entreprise) { + if (instance.connection.openerp_entreprise) { buttons[_t("Send OpenERP Enterprise Report")] = function() { var $this = $(this); var issuename = $('#issuename').val(); var explanation = $('#explanation').val(); var remark = $('#remark').val(); // Call the send method from server to send mail with details - new openerp.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){ + new instance.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){ if (result === false) { alert('There was a communication error.') } else { @@ -205,7 +195,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ $(this).dialog("close"); }; } - var dialog = new openerp.web.Dialog(this, { + var dialog = new instance.web.Dialog(this, { title: "OpenERP " + _.str.capitalize(error.type), width: '80%', height: '50%', @@ -213,19 +203,12 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ min_height: '600px', buttons: buttons }).open(); - dialog.$element.html(QWeb.render('CrashManagerError', {session: openerp.connection, error: error})); + dialog.$element.html(QWeb.render('CrashManager.error', {session: instance.connection, error: error})); } }); -openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loading# */{ +instance.web.Loading = instance.web.Widget.extend({ template: 'Loading', - /** - * @constructs openerp.web.Loading - * @extends openerp.web.OldWidget - * - * @param parent - * @param element_id - */ init: function(parent) { this._super(parent); this.count = 0; @@ -258,10 +241,9 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin this.count += increment; if (this.count > 0) { - $(".loading",this.$element).text(_.str.sprintf( - _t("Loading (%d)"), this.count)); - $(".loading",this.$element).show(); - this.getParent().$element.addClass('loading'); + this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count)); + this.$element.show(); + this.getParent().$element.addClass('oe_wait'); } else { this.count = 0; clearTimeout(this.long_running_timer); @@ -270,73 +252,60 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin this.blocked_ui = false; $.unblockUI(); } - $(".loading",this.$element).fadeOut(); - this.getParent().$element.removeClass('loading'); + this.$element.fadeOut(); + this.getParent().$element.removeClass('oe_wait'); } } }); -openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Database# */{ - template: "DatabaseManager", - /** - * @constructs openerp.web.Database - * @extends openerp.web.OldWidget - * - * @param parent - * @param element_id - * @param option_id - */ - init: function(parent, element_id, option_id) { - this._super(parent, element_id); +instance.web.DatabaseManager = instance.web.Widget.extend({ + init: function(parent) { + this._super(parent); this.unblockUIFunction = $.unblockUI; $.validator.addMethod('matches', function (s, _, re) { return new RegExp(re).test(s); }, _t("Invalid database name")); }, start: function() { - this.$option_id = $("#oe_db_options"); - var self = this; var fetch_db = this.rpc("/web/database/get_list", {}).pipe( - function(result) { self.db_list = result.db_list; }, - function (_, ev) { ev.preventDefault(); self.db_list = null; }); - var fetch_langs = this.rpc("/web/session/get_lang_list", {}, function(result) { - if (result.error) { - self.display_error(result); - return; - } + function(result) { + self.db_list = result.db_list; + }, + function (_, ev) { + ev.preventDefault(); + self.db_list = null; + }); + var fetch_langs = this.rpc("/web/session/get_lang_list", {}).then(function(result) { self.lang_list = result.lang_list; }); - $.when(fetch_db, fetch_langs).then(function () {self.do_create();}); - - this.$element.find('#db-create').click(this.do_create); - this.$element.find('#db-drop').click(this.do_drop); - this.$element.find('#db-backup').click(this.do_backup); - this.$element.find('#db-restore').click(this.do_restore); - this.$element.find('#db-change-password').click(this.do_change_password); - this.$element.find('#back-to-login').click(function() { - self.hide(); + return $.when(fetch_db, fetch_langs).then(self.do_render); + }, + do_render: function() { + var self = this; + self.$element.html(QWeb.render("DatabaseManager", { widget : self })); + self.$element.find(".oe_database_manager_menu").tabs(); + self.$element.find("form[name=create_db_form]").validate({ submitHandler: self.do_create }); + self.$element.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop }); + self.$element.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup }); + self.$element.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore }); + self.$element.find("form[name=change_pwd_form]").validate({ + messages: { + old_pwd: "Please enter your previous password", + new_pwd: "Please enter your new password", + confirm_pwd: { + required: "Please confirm your new password", + equalTo: "The confirmation does not match the password" + } + }, + submitHandler: self.do_change_password }); + self.$element.find("#back_to_login").click(self.do_exit); }, destroy: function () { - this.hide(); - this.$option_id.empty(); - - this.$element - .find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login') - .unbind('click') - .end() - .empty(); + this.$element.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty(); this._super(); }, - show: function () { - this.$element.closest(".login") - .addClass("database_block"); - }, - hide: function () { - this.$element.closest(".login") - .removeClass("database_block") - }, /** * Converts a .serializeArray() result into a dict. Does not bother folding * multiple identical keys into an array, last key wins. @@ -375,7 +344,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab * @param {String} error.error message of the error dialog */ display_error: function (error) { - return openerp.web.dialog($('<div>'), { + return instance.web.dialog($('<div>'), { modal: true, title: error.title, buttons: [ @@ -383,157 +352,96 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab ] }).html(error.error); }, - do_create: function() { + do_create: function(form) { var self = this; - self.$option_id.html(QWeb.render("Database.CreateDB", self)); - self.$option_id.find("form[name=create_db_form]").validate({ - submitHandler: function (form) { - var fields = $(form).serializeArray(); - self.rpc("/web/database/create", {'fields': fields}, function(result) { - if (self.db_list) { - self.db_list.push(self.to_object(fields)['db_name']); - self.db_list.sort(); - self.getParent().set_db_list(self.db_list); - } - - var form_obj = self.to_object(fields); - self.getParent().do_login( - form_obj['db_name'], - 'admin', - form_obj['create_admin_pwd']); - self.destroy(); - }); - } + var fields = $(form).serializeArray(); + self.rpc("/web/database/create", {'fields': fields}, function(result) { + var form_obj = self.to_object(fields); + self.getParent().do_login( form_obj['db_name'], 'admin', form_obj['create_admin_pwd']); + self.destroy(); }); + }, - do_drop: function() { + do_drop: function(form) { var self = this; - self.$option_id.html(QWeb.render("DropDB", self)); - self.$option_id.find("form[name=drop_db_form]").validate({ - submitHandler: function (form) { - var $form = $(form), - fields = $form.serializeArray(), - $db_list = $form.find('[name=drop_db]'), - db = $db_list.val(); - - if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) { - return; - } - self.rpc("/web/database/drop", {'fields': fields}, function(result) { - if (result.error) { - self.display_error(result); - return; - } - $db_list.find(':selected').remove(); - if (self.db_list) { - self.db_list.splice(_.indexOf(self.db_list, db, true), 1); - self.getParent().set_db_list(self.db_list); - } - self.do_notify("Dropping database", "The database '" + db + "' has been dropped"); - }); + var $form = $(form), + fields = $form.serializeArray(), + $db_list = $form.find('[name=drop_db]'), + db = $db_list.val(); + if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) { + return; + } + self.rpc("/web/database/drop", {'fields': fields}, function(result) { + if (result.error) { + self.display_error(result); + return; } + self.do_notify("Dropping database", "The database '" + db + "' has been dropped"); + self.start(); }); }, - do_backup: function() { + do_backup: function(form) { var self = this; - self.$option_id - .html(QWeb.render("BackupDB", self)) - .find("form[name=backup_db_form]").validate({ - submitHandler: function (form) { - self.blockUI(); - self.session.get_file({ - form: form, - success: function () { - self.do_notify(_t("Backed"), - _t("Database backed up successfully")); - }, - error: openerp.webclient.crashmanager.on_rpc_error, - complete: function() { - self.unblockUI(); - } - }); + self.blockUI(); + self.session.get_file({ + form: form, + success: function () { + self.do_notify(_t("Backed"), _t("Database backed up successfully")); + }, + error: instance.webclient.crashmanager.on_rpc_error, + complete: function() { + self.unblockUI(); } }); }, - do_restore: function() { + do_restore: function(form) { var self = this; - self.$option_id.html(QWeb.render("RestoreDB", self)); + self.blockUI(); + $(form).ajaxSubmit({ + url: '/web/database/restore', + type: 'POST', + resetForm: true, + success: function (body) { + // If empty body, everything went fine + if (!body) { return; } - self.$option_id.find("form[name=restore_db_form]").validate({ - submitHandler: function (form) { - self.blockUI(); - $(form).ajaxSubmit({ - url: '/web/database/restore', - type: 'POST', - resetForm: true, - success: function (body) { - // TODO: ui manipulations - // note: response objects don't work, but we have the - // HTTP body of the response~~ - - // If empty body, everything went fine - if (!body) { return; } - - if (body.indexOf('403 Forbidden') !== -1) { - self.display_error({ - title: 'Access Denied', - error: 'Incorrect super-administrator password' - }) - } else { - self.display_error({ - title: 'Restore Database', - error: 'Could not restore the database' - }) - } - }, - complete: function() { - self.unblockUI(); - self.do_notify(_t("Restored"), _t("Database restored successfully")); - } - }); + if (body.indexOf('403 Forbidden') !== -1) { + self.display_error({ + title: 'Access Denied', + error: 'Incorrect super-administrator password' + }) + } else { + self.display_error({ + title: 'Restore Database', + error: 'Could not restore the database' + }) + } + }, + complete: function() { + self.unblockUI(); + self.do_notify(_t("Restored"), _t("Database restored successfully")); } }); }, - do_change_password: function() { + do_change_password: function(form) { var self = this; - self.$option_id.html(QWeb.render("Change_DB_Pwd", self)); - - self.$option_id.find("form[name=change_pwd_form]").validate({ - messages: { - old_pwd: "Please enter your previous password", - new_pwd: "Please enter your new password", - confirm_pwd: { - required: "Please confirm your new password", - equalTo: "The confirmation does not match the password" - } - }, - submitHandler: function (form) { - self.rpc("/web/database/change_password", { - 'fields': $(form).serializeArray() - }, function(result) { - if (result.error) { - self.display_error(result); - return; - } - self.do_notify("Changed Password", "Password has been changed successfully"); - }); + self.rpc("/web/database/change_password", { + 'fields': $(form).serializeArray() + }, function(result) { + if (result.error) { + self.display_error(result); + return; } + self.do_notify("Changed Password", "Password has been changed successfully"); }); + }, + do_exit: function () { } }); -openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# */{ - remember_credentials: true, - +instance.web.Login = instance.web.Widget.extend({ template: "Login", - /** - * @constructs openerp.web.Login - * @extends openerp.web.OldWidget - * - * @param parent - * @param element_id - */ - + remember_credentials: true, init: function(parent) { this._super(parent); this.has_local_storage = typeof(localStorage) != 'undefined'; @@ -550,29 +458,35 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# }, start: function() { var self = this; - this.database = new openerp.web.Database(this); - this.database.appendTo(this.$element); - this.$element.find('#oe-db-config').click(function() { - self.database.show(); - }); + self.$element.find("form").submit(self.on_submit); - this.$element.find("form").submit(this.on_submit); - - this.rpc("/web/database/get_list", {}, function(result) { + self.$element.find('.oe_login_manage_db').click(function() { + self.$element.find('.oe_login_bottom').hide(); + self.$element.find('.oe_login_pane').hide(); + self.databasemanager = new instance.web.DatabaseManager(self); + self.databasemanager.appendTo(self.$element); + self.databasemanager.do_exit.add_last(function() { + self.databasemanager.destroy(); + self.$element.find('.oe_login_bottom').show(); + self.$element.find('.oe_login_pane').show(); + self.load_db_list(); + }) + }); + self.load_db_list(); + }, + load_db_list: function () { + var self = this; + self.rpc("/web/database/get_list", {}, function(result) { self.set_db_list(result.db_list); - }, - function(error, event) { + }, function(error, event) { if (error.data.fault_code === 'AccessDenied') { event.preventDefault(); } }); - }, set_db_list: function (list) { - this.$element.find("[name=db]").replaceWith( - openerp.web.qweb.render('Login_dblist', { - db_list: list, selected_db: this.selected_db})) + this.$element.find("[name=db]").replaceWith(instance.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db})) }, on_submit: function(ev) { if(ev) { @@ -598,15 +512,15 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# */ do_login: function (db, login, password) { var self = this; - this.$element.removeClass('login_invalid'); + this.$element.removeClass('oe_login_invalid'); this.session.on_session_invalid.add({ callback: function () { - self.$element.addClass("login_invalid"); + self.$element.addClass("oe_login_invalid"); }, unique: true }); this.session.session_authenticate(db, login, password).then(function() { - self.$element.removeClass("login_invalid"); + self.$element.removeClass("oe_login_invalid"); if (self.has_local_storage) { if(self.remember_credentials) { localStorage.setItem('last_db_login_success', db); @@ -624,13 +538,7 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# } }); -openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ - /** - * @constructs openerp.web.Menu - * @extends openerp.web.Widget - * - * @param parent - */ +instance.web.Menu = instance.web.Widget.extend({ template: 'Menu', init: function() { this._super.apply(this, arguments); @@ -774,14 +682,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ } }); -openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMenu# */{ +instance.web.UserMenu = instance.web.Widget.extend({ template: "UserMenu", - /** - * @constructs openerp.web.UserMenu - * @extends openerp.web.Widget - * - * @param parent - */ init: function(parent) { this._super(parent); this.update_promise = $.Deferred().resolve(); @@ -805,7 +707,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen }, change_password :function() { var self = this; - this.dialog = new openerp.web.Dialog(this, { + this.dialog = new instance.web.Dialog(this, { title: _t("Change Password"), width : 'auto' }).open(); @@ -819,14 +721,14 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen self.display_error(result); return; } else { - openerp.webclient.on_logout(); + instance.webclient.on_logout(); } }); } }); }, display_error: function (error) { - return openerp.web.dialog($('<div>'), { + return instance.web.dialog($('<div>'), { modal: true, title: error.title, buttons: [ @@ -841,10 +743,10 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen $avatar.attr('src', $avatar.data('default-src')); if (!self.session.uid) return; - var func = new openerp.web.Model("res.users").get_func("read"); + var func = new instance.web.Model("res.users").get_func("read"); return func(self.session.uid, ["name", "company_id"]).pipe(function(res) { // TODO: Show company if multicompany is in use - var topbar_name = _.str.sprintf("%s (%s)", res.name, openerp.connection.db, res.company_id[1]); + var topbar_name = _.str.sprintf("%s (%s)", res.name, instance.connection.db, res.company_id[1]); self.$element.find('.oe_topbar_name').text(topbar_name); var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=avatar&id=%s', self.session.prefix, self.session.session_id, self.session.uid); $avatar.attr('src', avatar_src); @@ -858,7 +760,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen shortcut_load :function(){ var self = this, sc = self.session.shortcuts, - shortcuts_ds = new openerp.web.DataSet(this, 'ir.ui.view_sc'); + shortcuts_ds = new instance.web.DataSet(this, 'ir.ui.view_sc'); self.$element.find('.oe_dropdown_options a[data-menu=shortcut]').each(function() { $(this).parent().remove(); }); @@ -905,11 +807,12 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen }, on_menu_settings: function() { var self = this; - var action_manager = new openerp.web.ActionManager(this); - var dataset = new openerp.web.DataSet (this,'res.users',this.context); + var action_manager = new instance.web.ActionManager(this); + var dataset = new instance.web.DataSet (this,'res.users',this.context); dataset.call ('action_get','',function (result){ self.rpc('/web/action/load', {action_id:result}, function(result){ action_manager.do_action(_.extend(result['result'], { + target: 'inline', res_id: self.session.uid, res_model: 'res.users', flags: { @@ -922,7 +825,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen })); }); }); - this.dialog = new openerp.web.Dialog(this,{ + this.dialog = new instance.web.Dialog(this,{ title: _t("Preferences"), width: '700px', buttons: [ @@ -941,7 +844,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen ] }).open(); action_manager.appendTo(this.dialog.$element); - action_manager.render(this.dialog); + action_manager.renderElement(this.dialog); }, on_menu_about: function() { var self = this; @@ -952,7 +855,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen window.location = $.param.querystring( window.location.href, 'debug'); }); - openerp.web.dialog($help, {autoOpen: true, + instance.web.dialog($help, {autoOpen: true, modal: true, width: 960, title: _t("About")}); }); }, @@ -968,21 +871,17 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen } }); -openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{ - /** - * @constructs openerp.web.WebClient - * @extends openerp.web.Widget - */ +instance.web.WebClient = instance.web.Widget.extend({ init: function(parent) { var self = this; this._super(parent); - openerp.webclient = this; + instance.webclient = this; this.querystring = '?' + jQuery.param.querystring(); this._current_state = null; }, start: function() { var self = this; - this.$element.addClass("openerp openerp2"); + this.$element.addClass("openerp openerp-web-client-container"); if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) { this.$element.addClass("kitten-mode-activated"); this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) { @@ -1001,7 +900,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.menu.do_reload(); if(self.action_manager) self.action_manager.destroy(); - self.action_manager = new openerp.web.ActionManager(self); + self.action_manager = new instance.web.ActionManager(self); self.action_manager.appendTo(self.$element.find('.oe_application')); self.bind_hashchange(); var version_label = _t("OpenERP - Unsupported/Community Version"); @@ -1018,7 +917,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie var self = this; this.destroy_content(); this.show_common(); - self.login = new openerp.web.Login(self); + self.login = new instance.web.Login(self); self.login.appendTo(self.$element); }, show_application: function() { @@ -1027,10 +926,10 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie this.show_common(); self.$table = $(QWeb.render("WebClient", {})); self.$element.append(self.$table); - self.menu = new openerp.web.Menu(self); + self.menu = new instance.web.Menu(self); self.menu.replace(this.$element.find('.oe_menu_placeholder')); self.menu.on_action.add(this.proxy('on_menu_action')); - self.user_menu = new openerp.web.UserMenu(self); + self.user_menu = new instance.web.UserMenu(self); self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder')); self.user_menu.on_menu_logout.add(this.proxy('on_logout')); self.user_menu.on_action.add(this.proxy('on_menu_action')); @@ -1038,8 +937,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie show_common: function() { var self = this; if (!this.crashmanager) { - this.crashmanager = new openerp.web.CrashManager(); - openerp.connection.on_rpc_error.add(this.crashmanager.on_rpc_error); + this.crashmanager = new instance.web.CrashManager(); + instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error); window.onerror = function (message, file, line) { self.crashmanager.on_traceback({ type: _t("Client Error"), @@ -1048,9 +947,9 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie }); } } - this.notification = new openerp.web.Notification(this); + this.notification = new instance.web.Notification(this); this.notification.appendTo(this.$element); - this.loading = new openerp.web.Loading(this); + this.loading = new instance.web.Loading(this); this.loading.appendTo(this.$element); }, destroy_content: function() { @@ -1062,7 +961,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie do_reload: function() { var self = this; return this.session.session_reload().pipe(function () { - openerp.connection.load_modules(true).pipe( + instance.connection.load_modules(true).pipe( self.menu.proxy('do_reload')); }); }, @@ -1126,16 +1025,15 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie } }); -openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({ +instance.web.EmbeddedClient = instance.web.Widget.extend({ template: 'EmptyComponent', - init: function(action_id, options) { - this._super(); + init: function(parent, action_id, options) { + this._super(parent); // TODO take the xmlid of a action instead of its id this.action_id = action_id; this.options = options || {}; - this.am = new openerp.web.ActionManager(this); + this.am = new instance.web.ActionManager(this); }, - start: function() { var self = this; this.am.appendTo(this.$element.addClass('openerp')); @@ -1152,10 +1050,9 @@ openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({ self.am.do_action(action); }); } - }); -openerp.web.embed = function (origin, dbname, login, key, action, options) { +instance.web.embed = function (origin, dbname, login, key, action, options) { $('head').append($('<link>', { 'rel': 'stylesheet', 'type': 'text/css', @@ -1166,9 +1063,9 @@ openerp.web.embed = function (origin, dbname, login, key, action, options) { var sc = document.getElementsByTagName('script'); currentScript = sc[sc.length-1]; } - openerp.connection.session_bind(origin).then(function () { - openerp.connection.session_authenticate(dbname, login, key, true).then(function () { - var client = new openerp.web.EmbeddedClient(action, options); + instance.connection.session_bind(origin).then(function () { + instance.connection.session_authenticate(dbname, login, key, true).then(function () { + var client = new instance.web.EmbeddedClient(null, action, options); client.insertAfter(currentScript); }); }); diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/corelib.js index 44e15f6dd13..cbd28aca8a3 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/corelib.js @@ -1,60 +1,134 @@ -/*--------------------------------------------------------- - * OpenERP Web core - *--------------------------------------------------------*/ -var console; -if (!console) { - console = {log: function () {}}; -} -if (!console.debug) { - console.debug = console.log; -} +/* + * Copyright (c) 2012, OpenERP S.A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -openerp.web.core = function(openerp) { +openerp.web.corelib = function(instance) { -// a function to override the "extend()" method of JR's inheritance, allowing -// the usage of "include()" -oe_override_class = function(claz){ - var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? - /\b_super\b/ : /.*/; - - // Create a new Class that inherits from this class - claz.extend = function(prop) { +/** + * Improved John Resig's inheritance, based on: + * + * Simple JavaScript Inheritance + * By John Resig http://ejohn.org/ + * MIT Licensed. + * + * Adds "include()" + * + * Defines The Class object. That object can be used to define and inherit classes using + * the extend() method. + * + * Example: + * + * var Person = instance.web.Class.extend({ + * init: function(isDancing){ + * this.dancing = isDancing; + * }, + * dance: function(){ + * return this.dancing; + * } + * }); + * + * The init() method act as a constructor. This class can be instancied this way: + * + * var person = new Person(true); + * person.dance(); + * + * The Person class can also be extended again: + * + * var Ninja = Person.extend({ + * init: function(){ + * this._super( false ); + * }, + * dance: function(){ + * // Call the inherited version of dance() + * return this._super(); + * }, + * swingSword: function(){ + * return true; + * } + * }); + * + * When extending a class, each re-defined method can use this._super() to call the previous + * implementation of that method. + */ +(function() { + var initializing = false, + fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; + // The web Class implementation (does nothing) + instance.web.Class = function(){}; + + /** + * Subclass an existing class + * + * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class + */ + instance.web.Class.extend = function() { var _super = this.prototype; - - // Instantiate a base class (but only create the instance, don't run the - // init constructor) - initializing = true; var prototype = new this(); initializing = false; - + // Support mixins arguments + var args = _.toArray(arguments); + args.unshift({}); + var prop = _.extend.apply(_,args); + + // Instantiate a web class (but only create the instance, + // don't run the init constructor) + initializing = true; + var prototype = new this(); + initializing = false; + // Copy the properties over onto the new prototype for (var name in prop) { - // Check if we're overwriting an existing function - prototype[name] = typeof prop[name] == "function" && - typeof _super[name] == "function" && fnTest.test(prop[name]) ? - (function(name, fn){ - return function() { - var tmp = this._super; - - // Add a new ._super() method that is the same method but on the - // super-class - this._super = _super[name]; - - // The method only need to be bound temporarily, so we remove it - // when we're done executing - var ret = fn.apply(this, arguments); this._super = tmp; - - return ret; - }; - })(name, prop[name]) : prop[name]; + // Check if we're overwriting an existing function + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && + fnTest.test(prop[name]) ? + (function(name, fn) { + return function() { + var tmp = this._super; + + // Add a new ._super() method that is the same + // method but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so + // we remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]) : + prop[name]; } - + // The dummy class constructor function Class() { // All construction is actually done in the init method if (!initializing && this.init) { - var ret = this.init.apply(this, arguments); if (ret) { return ret;} - } return this; + var ret = this.init.apply(this, arguments); + if (ret) { return ret; } + } + return this; } - Class.include = function (properties) { for (var name in properties) { if (typeof properties[name] !== 'function' @@ -64,138 +138,617 @@ oe_override_class = function(claz){ && prototype.hasOwnProperty(name)) { prototype[name] = (function (name, fn, previous) { return function () { - var tmp = this._super; this._super = previous; var - ret = fn.apply(this, arguments); this._super = tmp; + var tmp = this._super; + this._super = previous; + var ret = fn.apply(this, arguments); + this._super = tmp; return ret; } })(name, properties[name], prototype[name]); } else if (typeof _super[name] === 'function') { prototype[name] = (function (name, fn) { return function () { - var tmp = this._super; this._super = _super[name]; - var ret = fn.apply(this, arguments); this._super = - tmp; return ret; + var tmp = this._super; + this._super = _super[name]; + var ret = fn.apply(this, arguments); + this._super = tmp; + return ret; } })(name, properties[name]); } } }; - + // Populate our constructed prototype object Class.prototype = prototype; - + // Enforce the constructor to be what we expect - Class.prototype.constructor = Class; - + Class.constructor = Class; + // And make this class extendable Class.extend = arguments.callee; - + return Class; }; -}; -oe_override_class(nova.Class); -oe_override_class(nova.Widget); +})(); -openerp.web.Class = nova.Class; +// Mixins -openerp.web.callback = function(obj, method) { - var callback = function() { - var args = Array.prototype.slice.call(arguments); - var r; - for(var i = 0; i < callback.callback_chain.length; i++) { - var c = callback.callback_chain[i]; - if(c.unique) { - callback.callback_chain.splice(i, 1); - i -= 1; - } - var result = c.callback.apply(c.self, c.args.concat(args)); - if (c.callback === method) { - // return the result of the original method - r = result; +/** + * Mixin to structure objects' life-cycles folowing a parent-children + * relationship. Each object can a have a parent and multiple children. + * When an object is destroyed, all its children are destroyed too releasing + * any resource they could have reserved before. + */ +instance.web.ParentedMixin = { + __parentedMixin : true, + init: function() { + this.__parentedDestroyed = false; + this.__parentedChildren = []; + this.__parentedParent = null; + }, + /** + * Set the parent of the current object. When calling this method, the + * parent will also be informed and will return the current object + * when its getChildren() method is called. If the current object did + * already have a parent, it is unregistered before, which means the + * previous parent will not return the current object anymore when its + * getChildren() method is called. + */ + setParent : function(parent) { + if (this.getParent()) { + if (this.getParent().__parentedMixin) { + this.getParent().__parentedChildren = _.without(this + .getParent().getChildren(), this); } - // TODO special value to stop the chain - // openerp.web.callback_stop - } - return r; - }; - callback.callback_chain = []; - callback.add = function(f) { - if(typeof(f) == 'function') { - f = { callback: f, args: Array.prototype.slice.call(arguments, 1) }; } - f.self = f.self || null; - f.args = f.args || []; - f.unique = !!f.unique; - if(f.position == 'last') { - callback.callback_chain.push(f); - } else { - callback.callback_chain.unshift(f); + this.__parentedParent = parent; + if (parent && parent.__parentedMixin) { + parent.__parentedChildren.push(this); } - return callback; - }; - callback.add_first = function(f) { - return callback.add.apply(null,arguments); - }; - callback.add_last = function(f) { - return callback.add({ - callback: f, - args: Array.prototype.slice.call(arguments, 1), - position: "last" + }, + /** + * Return the current parent of the object (or null). + */ + getParent : function() { + return this.__parentedParent; + }, + /** + * Return a list of the children of the current object. + */ + getChildren : function() { + return _.clone(this.__parentedChildren); + }, + /** + * Returns true if destroy() was called on the current object. + */ + isDestroyed : function() { + return this.__parentedDestroyed; + }, + /** + * Inform the object it should destroy itself, releasing any + * resource it could have reserved. + */ + destroy : function() { + _.each(this.getChildren(), function(el) { + el.destroy(); }); - }; - callback.remove = function(f) { - callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) { - return el.callback === f; - })); - return callback; - }; - - return callback.add({ - callback: method, - self:obj, - args:Array.prototype.slice.call(arguments, 2) - }); + this.setParent(undefined); + this.__parentedDestroyed = true; + } }; /** - * web error for lookup failure + * TODO al: move into the the mixin + * + * Backbone's events + * + * (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. * - * @class */ -openerp.web.NotFound = openerp.web.Class.extend( /** @lends openerp.web.NotFound# */ { +instance.web.Events = instance.web.Class.extend({ + + on : function(events, callback, context) { + var ev; + events = events.split(/\s+/); + var calls = this._callbacks || (this._callbacks = {}); + while (ev = events.shift()) { + var list = calls[ev] || (calls[ev] = {}); + var tail = list.tail || (list.tail = list.next = {}); + tail.callback = callback; + tail.context = context; + list.tail = tail.next = {}; + } + return this; + }, + + off : function(events, callback, context) { + var ev, calls, node; + if (!events) { + delete this._callbacks; + } else if (calls = this._callbacks) { + events = events.split(/\s+/); + while (ev = events.shift()) { + node = calls[ev]; + delete calls[ev]; + if (!callback || !node) + continue; + while ((node = node.next) && node.next) { + if (node.callback === callback + && (!context || node.context === context)) + continue; + this.on(ev, node.callback, node.context); + } + } + } + return this; + }, + + trigger : function(events) { + var event, node, calls, tail, args, all, rest; + if (!(calls = this._callbacks)) + return this; + all = calls['all']; + (events = events.split(/\s+/)).push(null); + // Save references to the current heads & tails. + while (event = events.shift()) { + if (all) + events.push({ + next : all.next, + tail : all.tail, + event : event + }); + if (!(node = calls[event])) + continue; + events.push({ + next : node.next, + tail : node.tail + }); + } + rest = Array.prototype.slice.call(arguments, 1); + while (node = events.pop()) { + tail = node.tail; + args = node.event ? [ node.event ].concat(rest) : rest; + while ((node = node.next) !== tail) { + node.callback.apply(node.context || this, args); + } + } + return this; + } +}); + +instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, { + __eventDispatcherMixin: true, + init: function() { + instance.web.ParentedMixin.init.call(this); + this.__edispatcherEvents = new instance.web.Events(); + this.__edispatcherRegisteredEvents = []; + }, + on: function(events, dest, func) { + var self = this; + events = events.split(/\s+/); + _.each(events, function(eventName) { + self.__edispatcherEvents.on(eventName, func, dest); + if (dest && dest.__eventDispatcherMixin) { + dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self}); + } + }); + return this; + }, + off: function(events, dest, func) { + var self = this; + events = events.split(/\s+/); + _.each(events, function(eventName) { + self.__edispatcherEvents.off(eventName, func, dest); + if (dest && dest.__eventDispatcherMixin) { + dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) { + return !(el.name === eventName && el.func === func && el.source === self); + }); + } + }); + return this; + }, + trigger: function(events) { + this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments); + return this; + }, + destroy: function() { + var self = this; + _.each(this.__edispatcherRegisteredEvents, function(event) { + event.source.__edispatcherEvents.off(event.name, event.func, self); + }); + this.__edispatcherRegisteredEvents = []; + if(!this.__edispatcherEvents) { + debugger; + } + this.__edispatcherEvents.off(); + instance.web.ParentedMixin.destroy.call(this); + } +}); + +instance.web.GetterSetterMixin = _.extend({}, instance.web.EventDispatcherMixin, { + init: function() { + instance.web.EventDispatcherMixin.init.call(this); + this.__getterSetterInternalMap = {}; + }, + set: function(map) { + var self = this; + var changed = false; + _.each(map, function(val, key) { + var tmp = self.__getterSetterInternalMap[key]; + if (tmp === val) + return; + changed = true; + self.__getterSetterInternalMap[key] = val; + self.trigger("change:" + key, self, { + oldValue: tmp, + newValue: val + }); + }); + if (changed) + self.trigger("change", self); + }, + get: function(key) { + return this.__getterSetterInternalMap[key]; + } +}); + +instance.web.CallbackEnabledMixin = _.extend({}, instance.web.GetterSetterMixin, { + init: function() { + instance.web.GetterSetterMixin.init.call(this); + var self = this; + var callback_maker = function(obj, name, method) { + var callback = function() { + var args = Array.prototype.slice.call(arguments); + self.trigger.apply(self, [name].concat(args)); + var r; + for(var i = 0; i < callback.callback_chain.length; i++) { + var c = callback.callback_chain[i]; + if(c.unique) { + callback.callback_chain.splice(i, 1); + i -= 1; + } + var result = c.callback.apply(c.self, c.args.concat(args)); + if (c.callback === method) { + // return the result of the original method + r = result; + } + // TODO special value to stop the chain + // instance.web.callback_stop + } + return r; + }; + callback.callback_chain = []; + callback.add = function(f) { + if(typeof(f) == 'function') { + f = { callback: f, args: Array.prototype.slice.call(arguments, 1) }; + } + f.self = f.self || null; + f.args = f.args || []; + f.unique = !!f.unique; + if(f.position == 'last') { + callback.callback_chain.push(f); + } else { + callback.callback_chain.unshift(f); + } + return callback; + }; + callback.add_first = function(f) { + return callback.add.apply(null,arguments); + }; + callback.add_last = function(f) { + return callback.add({ + callback: f, + args: Array.prototype.slice.call(arguments, 1), + position: "last" + }); + }; + callback.remove = function(f) { + callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) { + return el.callback === f; + })); + return callback; + }; + + return callback.add({ + callback: method, + self:obj, + args:Array.prototype.slice.call(arguments, 3) + }); + }; + // Transform on_/do_* methods into callbacks + for (var name in this) { + if(typeof(this[name]) == "function") { + this[name].debug_name = name; + if((/^on_|^do_/).test(name)) { + this[name] = callback_maker(this, name, 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} method_name name of the method to invoke + * @returns {Function} proxied method + */ + proxy: function (method_name) { + var self = this; + return function () { + return self[method_name].apply(self, arguments); + } + } }); -openerp.web.KeyNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.KeyNotFound# */ { + +instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, { + /** + * Tag name when creating a default $element. + * @type string + */ + tagName: 'div', /** - * Thrown when a key could not be found in a mapping + * Constructs the widget and sets its parent if a parent is given. + * + * @constructs instance.web.Widget + * @extends instance.web.CallbackEnabled * - * @constructs openerp.web.KeyNotFound - * @extends openerp.web.NotFound - * @param {String} key the key which could not be found + * @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. + * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want + * to bind the current Widget to an already existing part of the DOM, which is not compatible + * with the DOM insertion methods provided by the current implementation of Widget. So + * for new components this argument should not be provided any more. */ - init: function (key) { - this.key = key; + init: function(parent) { + instance.web.CallbackEnabledMixin.init.call(this); + this.$element = $(document.createElement(this.tagName)); + this.setParent(parent); + }, + /** + * Destroys the current widget, also destroys all its children before destroying itself. + */ + destroy: function() { + _.each(this.getChildren(), function(el) { + el.destroy(); + }); + if(this.$element != null) { + this.$element.remove(); + } + instance.web.GetterSetterMixin.destroy.call(this); + }, + /** + * Renders the current widget and appends it to the given jQuery object or Widget. + * + * @param target A jQuery object or a Widget instance. + */ + appendTo: function(target) { + var self = this; + return this.__widgetRenderAndInsert(function(t) { + self.$element.appendTo(t); + }, target); + }, + /** + * Renders the current widget and prepends it to the given jQuery object or Widget. + * + * @param target A jQuery object or a Widget instance. + */ + prependTo: function(target) { + var self = this; + return this.__widgetRenderAndInsert(function(t) { + self.$element.prependTo(t); + }, target); + }, + /** + * Renders the current widget and inserts it after to the given jQuery object or Widget. + * + * @param target A jQuery object or a Widget instance. + */ + insertAfter: function(target) { + var self = this; + return this.__widgetRenderAndInsert(function(t) { + self.$element.insertAfter(t); + }, target); + }, + /** + * Renders the current widget and inserts it before to the given jQuery object or Widget. + * + * @param target A jQuery object or a Widget instance. + */ + insertBefore: function(target) { + var self = this; + return this.__widgetRenderAndInsert(function(t) { + self.$element.insertBefore(t); + }, target); + }, + /** + * Renders the current widget and replaces the given jQuery object. + * + * @param target A jQuery object or a Widget instance. + */ + replace: function(target) { + return this.__widgetRenderAndInsert(_.bind(function(t) { + this.$element.replaceAll(t); + }, this), target); }, - toString: function () { - return "The key " + this.key + " was not found"; + __widgetRenderAndInsert: function(insertion, target) { + this.renderElement(); + insertion(target); + return this.start(); + }, + /** + * This is the method to implement to render the Widget. + */ + renderElement: function() { + }, + /** + * Method called after rendering. Mostly used to bind actions, perform asynchronous + * calls, etc... + * + * By convention, the method should return a promise to inform the caller when + * this widget has been initialized. + * + * @returns {jQuery.Deferred} + */ + start: function() { + } +}); + +// Classes + +instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEnabledMixin, { + init: function() { + instance.web.CallbackEnabledMixin.init.call(this); } }); -openerp.web.ObjectNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.ObjectNotFound# */ { + +/** + * 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.$element` holds a correct value + * this.$element.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, { /** - * Thrown when an object path does not designate a valid class or object - * in the openerp hierarchy. + * 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 openerp.web.ObjectNotFound - * @extends openerp.web.NotFound - * @param {String} path the invalid object path + * @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. + * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want + * to bind the current Widget to an already existing part of the DOM, which is not compatible + * with the DOM insertion methods provided by the current implementation of Widget. So + * for new components this argument should not be provided any more. + */ + init: function(parent) { + instance.web.WidgetMixin.init.call(this,parent); + this.session = instance.connection; + }, + /** + * Renders the element. The default implementation renders the widget using QWeb, + * `this.template` must be defined. The context given to QWeb contains the "widget" + * key that references `this`. */ - init: function (path) { - this.path = path; + renderElement: function() { + var rendered = null; + if (this.template) + rendered = instance.web.qweb.render(this.template, {widget: this}); + if (_.str.trim(rendered)) { + var elem = $(rendered); + this.$element.replaceWith(elem); + this.$element = elem; + } }, - toString: function () { - return "Could not find any object of path " + this.path; + /** + * Informs the action manager to do an action. This supposes that + * the action manager can be found amongst the ancestors of the current widget. + * If that's not the case this method will simply return `false`. + */ + do_action: function(action, on_finished) { + if (this.getParent()) { + return this.getParent().do_action(action, on_finished); + } + return false; + }, + do_notify: function() { + if (this.getParent()) { + return this.getParent().do_notify.apply(this,arguments); + } + return false; + }, + do_warn: function() { + if (this.getParent()) { + return this.getParent().do_warn.apply(this,arguments); + } + return false; + }, + rpc: function(url, data, success, error) { + var def = $.Deferred().then(success, error); + var self = this; + instance.connection.rpc(url, data). then(function() { + if (!self.isDestroyed()) + def.resolve.apply(def, arguments); + }, function() { + if (!self.isDestroyed()) + def.reject.apply(def, arguments); + }); + return def.promise(); } }); -openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry# */ { + +instance.web.Registry = instance.web.Class.extend({ /** * Stores a mapping of arbitrary key (strings) to object paths (as strings * as well). @@ -204,11 +757,11 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * object, even if those objects have been overloaded/replaced after the * registry was created. * - * An object path is simply a dotted name from the openerp root to the - * object pointed to (e.g. ``"openerp.web.Connection"`` for an OpenERP + * An object path is simply a dotted name from the instance root to the + * object pointed to (e.g. ``"instance.web.Connection"`` for an OpenERP * connection object). * - * @constructs openerp.web.Registry + * @constructs instance.web.Registry * @param {Object} mapping a mapping of keys to object-paths */ init: function (mapping) { @@ -220,10 +773,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * * @param {String} key the key to fetch the object for * @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception - * @returns {Class} the stored class, to initialize - * - * @throws {openerp.web.KeyNotFound} if the object was not in the mapping - * @throws {openerp.web.ObjectNotFound} if the object path was invalid + * @returns {Class} the stored class, to initialize or null if not found */ get_object: function (key, silent_error) { var path_string = this.map[key]; @@ -232,10 +782,10 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry return this.parent.get_object(key, silent_error); } if (silent_error) { return void 'nooo'; } - throw new openerp.web.KeyNotFound(key); + return null; } - var object_match = openerp; + var object_match = instance; var path = path_string.split('.'); // ignore first section for(var i=1; i<path.length; ++i) { @@ -243,7 +793,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry if (object_match === undefined) { if (silent_error) { return void 'noooooo'; } - throw new openerp.web.ObjectNotFound(path_string); + return null; } } return object_match; @@ -269,9 +819,6 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * * @param {Array} keys a sequence of keys to fetch the object for * @returns {Class} the first class found matching an object - * - * @throws {openerp.web.KeyNotFound} if none of the keys was in the mapping - * @trows {openerp.web.ObjectNotFound} if a found object path was invalid */ get_any: function (keys) { for (var i=0; i<keys.length; ++i) { @@ -282,7 +829,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry return this.get_object(key); } - throw new openerp.web.KeyNotFound(keys.join(',')); + return null; }, /** * Adds a new key and value to the registry. @@ -291,7 +838,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * * @param {String} key * @param {String} object_path fully qualified dotted object path - * @returns {openerp.web.Registry} itself + * @returns {instance.web.Registry} itself */ add: function (key, object_path) { this.map[key] = object_path; @@ -307,7 +854,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * @param {Object} [mapping={}] a mapping of keys to object-paths */ extend: function (mapping) { - var child = new openerp.web.Registry(mapping); + var child = new instance.web.Registry(mapping); child.parent = this; return child; }, @@ -320,57 +867,10 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry } }); -openerp.web.CallbackEnabledMixin = { - init: function() { - // Transform on_* method into openerp.web.callbacks - for (var name in this) { - if(typeof(this[name]) == "function") { - this[name].debug_name = name; - // bind ALL function to this not only on_and _do ? - if((/^on_|^do_/).test(name)) { - this[name] = openerp.web.callback(this, 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} method_name name of the method to invoke - * @returns {Function} proxied method - */ - proxy: function (method_name) { - var self = this; - return function () { - return self[method_name].apply(self, arguments); - } - } -}; - -openerp.web.CallbackEnabled = openerp.web.Class.extend(_.extend({}, nova.GetterSetterMixin, openerp.web.CallbackEnabledMixin, { - init: function() { - nova.GetterSetterMixin.init.call(this); - openerp.web.CallbackEnabledMixin.init.call(this); - } -})); - -openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Connection# */{ +instance.web.Connection = instance.web.CallbackEnabled.extend( /** @lends instance.web.Connection# */{ /** - * @constructs openerp.web.Connection - * @extends openerp.web.CallbackEnabled + * @constructs instance.web.Connection + * @extends instance.web.CallbackEnabled * * @param {String} [server] JSON-RPC endpoint hostname * @param {String} [port] JSON-RPC endpoint port @@ -380,7 +880,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. this.server = null; this.debug = ($.deparam($.param.querystring()).debug != undefined); // TODO: session store in cookie should be optional - this.name = openerp._session_id; + this.name = instance._session_id; this.qweb_mutex = new $.Mutex(); }, session_bind: function(origin) { @@ -388,7 +888,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. this.origin = origin ? _.str.rtrim(origin,'/') : window_origin; this.prefix = this.origin; this.server = this.origin; // keep chs happy - openerp.web.qweb.default_dict['_s'] = this.origin; + instance.web.qweb.default_dict['_s'] = this.origin; this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp; this.session_id = false; this.uid = false; @@ -396,7 +896,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. this.user_context= {}; this.db = false; this.openerp_entreprise = false; - this.module_list = openerp._modules.slice(); + this.module_list = instance._modules.slice(); this.module_loaded = {}; _(this.module_list).each(function (mod) { self.module_loaded[mod] = true; @@ -569,7 +1069,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. try { var ctx = this.test_eval_contexts(source.contexts); if (!_.isEqual(ctx, expected.context)) { - openerp.webclient.notification.warn('Context mismatch, report to xmo', + instance.webclient.notification.warn('Context mismatch, report to xmo', _.str.sprintf(match_template, { source: JSON.stringify(source.contexts), local: JSON.stringify(ctx), @@ -577,17 +1077,17 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }), true); } } catch (e) { - openerp.webclient.notification.warn('Context fail, report to xmo', + instance.webclient.notification.warn('Context fail, report to xmo', _.str.sprintf(fail_template, { error: e.message, - source: source.contexts + source: JSON.stringify(source.contexts) }), true); } try { var dom = this.test_eval_domains(source.domains, this.test_eval_get_context()); if (!_.isEqual(dom, expected.domain)) { - openerp.webclient.notification.warn('Domains mismatch, report to xmo', + instance.webclient.notification.warn('Domains mismatch, report to xmo', _.str.sprintf(match_template, { source: JSON.stringify(source.domains), local: JSON.stringify(dom), @@ -595,17 +1095,17 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }), true); } } catch (e) { - openerp.webclient.notification.warn('Domain fail, report to xmo', + instance.webclient.notification.warn('Domain fail, report to xmo', _.str.sprintf(fail_template, { error: e.message, - source: source.domains + source: JSON.stringify(source.domains) }), true); } try { var groups = this.test_eval_groupby(source.group_by_seq); if (!_.isEqual(groups, expected.group_by)) { - openerp.webclient.notification.warn('GroupBy mismatch, report to xmo', + instance.webclient.notification.warn('GroupBy mismatch, report to xmo', _.str.sprintf(match_template, { source: JSON.stringify(source.group_by_seq), local: JSON.stringify(groups), @@ -613,43 +1113,51 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }), true); } } catch (e) { - openerp.webclient.notification.warn('GroupBy fail, report to xmo', + instance.webclient.notification.warn('GroupBy fail, report to xmo', _.str.sprintf(fail_template, { error: e.message, - source: source.group_by_seq + source: JSON.stringify(source.group_by_seq) }), true); } }, - test_eval_contexts: function (contexts) { - var result_context = _.extend({}, this.user_context), - self = this; - _(contexts).each(function (ctx) { + test_eval_contexts: function (contexts, evaluation_context) { + evaluation_context = evaluation_context || {}; + var self = this; + return _(contexts).reduce(function (result_context, ctx) { + // __eval_context evaluations can lead to some of `contexts`'s + // values being null, skip them as well as empty contexts + if (_.isEmpty(ctx)) { return result_context; } + var evaluated = ctx; switch(ctx.__ref) { case 'context': - _.extend(result_context, py.eval(ctx.__debug)); + evaluated = py.eval(ctx.__debug, evaluation_context); break; case 'compound_context': - _.extend( - result_context, self.test_eval_contexts(ctx.__contexts)); + var eval_context = self.test_eval_contexts([ctx.__eval_context]); + evaluated = self.test_eval_contexts( + ctx.__contexts, _.extend({}, evaluation_context, eval_context)); break; - default: - _.extend(result_context, ctx); } - }); - return result_context; + // add newly evaluated context to evaluation context for following + // siblings + _.extend(evaluation_context, evaluated); + return _.extend(result_context, evaluated); + }, _.extend({}, this.user_context)); }, - test_eval_domains: function (domains, eval_context) { + test_eval_domains: function (domains, evaluation_context) { var result_domain = [], self = this; _(domains).each(function (dom) { switch(dom.__ref) { case 'domain': result_domain.push.apply( - result_domain, py.eval(dom.__debug, eval_context)); + result_domain, py.eval(dom.__debug, evaluation_context)); break; case 'compound_domain': + var eval_context = self.test_eval_contexts([dom.__eval_context]); result_domain.push.apply( result_domain, self.test_eval_domains( - dom.__domains, eval_context)); + dom.__domains, _.extend( + {}, evaluation_context, eval_context))); break; default: result_domain.push.apply( @@ -667,7 +1175,8 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. group = py.eval(ctx.__debug).group_by; break; case 'compound_context': - group = self.test_eval_contexts(ctx.__contexts).group_by; + group = self.test_eval_contexts( + ctx.__contexts, ctx.__eval_context).group_by; break; default: group = ctx.group_by @@ -691,7 +1200,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. * setting the correct session id and session context in the parameter * objects * - * @param {Object} url RPC endpoint + * @param {String} url RPC endpoint * @param {Object} params call parameters * @param {Function} success_callback function to execute on RPC call success * @param {Function} error_callback function to execute on RPC call failure @@ -715,7 +1224,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }; var deferred = $.Deferred(); this.on_rpc_request(); - this.rpc_function(url, payload).then( + var aborter = params.aborter; + delete params.aborter; + var request = this.rpc_function(url, payload).then( function (response, textStatus, jqXHR) { self.on_rpc_response(); if (!response.error) { @@ -744,6 +1255,16 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }; deferred.reject(error, $.Event()); }); + if (aborter) { + aborter.abort_last = function () { + if (!(request.isResolved() || request.isRejected())) { + deferred.fail(function (error, event) { + event.preventDefault(); + }); + request.abort(); + } + }; + } // Allow deferred user to disable on_rpc_error in fail deferred.fail(function() { deferred.fail(function(error, event) { @@ -848,7 +1369,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. // TODO: session store in cookie should be optional this.session_id = this.get_cookie('session_id'); return this.session_reload().pipe(function(result) { - var modules = openerp._modules.join(','); + var modules = instance._modules.join(','); var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb); if(self.session_is_valid()) { return deferred.pipe(function() { return self.load_modules(); }); @@ -971,7 +1492,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css), self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb), self.rpc('/web/webclient/translations', params).pipe(function(trans) { - openerp.web._t.database.set_bundle(trans); + instance.web._t.database.set_bundle(trans); var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"]; return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) { return self.do_load_js(file_list.concat(files)); @@ -1033,7 +1554,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. self.qweb_mutex.exec(function() { return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) { if (!xml) { return; } - openerp.web.qweb.add_template(_.str.trim(xml)); + instance.web.qweb.add_template(_.str.trim(xml)); }); }); }); @@ -1044,10 +1565,10 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. var mod = this.module_list[j]; if(this.module_loaded[mod]) continue; - openerp[mod] = {}; + instance[mod] = {}; // init module mod - if(openerp._openerp[mod] != undefined) { - openerp._openerp[mod](openerp); + if(instance._openerp[mod] != undefined) { + instance._openerp[mod](instance); this.module_loaded[mod] = true; } } @@ -1167,164 +1688,10 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. } }); -/** - * 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 = openerp.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.$element` holds a correct value - * this.$element.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. - */ -openerp.web.Widget = nova.Widget.extend(_.extend({}, openerp.web.CallbackEnabledMixin, { - /** - * The name of the QWeb template that will be used for rendering. Must be - * redefined in subclasses or the default render() method can not be used. - * - * @type string - */ - template: null, - /** - * Constructs the widget and sets its parent if a parent is given. - * - * @constructs openerp.web.Widget - * @extends openerp.web.CallbackEnabled - * - * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance. - * When that widget is destroyed by calling destroy(), the current instance will be - * destroyed too. Can be null. - * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want - * to bind the current Widget to an already existing part of the DOM, which is not compatible - * with the DOM insertion methods provided by the current implementation of Widget. So - * for new components this argument should not be provided any more. - */ - init: function(parent) { - this._super(parent); - openerp.web.CallbackEnabledMixin.init.call(this); - this.session = openerp.connection; - }, - /** - * Renders the element. The default implementation renders the widget using QWeb, - * `this.template` must be defined. The context given to QWeb contains the "widget" - * key that references `this`. - */ - renderElement: function() { - var rendered = null; - if (this.template) - rendered = openerp.web.qweb.render(this.template, {widget: this}); - if (_.str.trim(rendered)) { - var elem = $(rendered); - this.$element.replaceWith(elem); - this.$element = elem; - } - }, - /** - * Informs the action manager to do an action. This supposes that - * the action manager can be found amongst the ancestors of the current widget. - * If that's not the case this method will simply return `false`. - */ - do_action: function(action, on_finished) { - if (this.getParent()) { - return this.getParent().do_action(action, on_finished); - } - return false; - }, - do_notify: function() { - if (this.getParent()) { - return this.getParent().do_notify.apply(this,arguments); - } - return false; - }, - do_warn: function() { - if (this.getParent()) { - return this.getParent().do_warn.apply(this,arguments); - } - return false; - }, - rpc: function(url, data, success, error) { - var def = $.Deferred().then(success, error); - var self = this; - openerp.connection.rpc(url, data). then(function() { - if (!self.isDestroyed()) - def.resolve.apply(def, arguments); - }, function() { - if (!self.isDestroyed()) - def.reject.apply(def, arguments); - }); - return def.promise(); - } -})); - -/** - * @deprecated use :class:`openerp.web.Widget` - */ -openerp.web.OldWidget = openerp.web.Widget.extend({ - init: function(parent, element_id) { - this._super(parent); - this.element_id = element_id; - this.element_id = this.element_id || _.uniqueId('widget-'); - var tmp = document.getElementById(this.element_id); - this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName)); - }, - renderElement: function() { - var rendered = this.render(); - if (rendered) { - var elem = $(rendered); - this.$element.replaceWith(elem); - this.$element = elem; - } - return this; - }, - render: function (additional) { - if (this.template) - return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {})); - return null; - } -}); - -openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.web.TranslationDataBase# */{ +instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{ /** - * @constructs openerp.web.TranslationDataBase - * @extends openerp.web.Class + * @constructs instance.web.TranslationDataBase + * @extends instance.web.Class */ init: function() { this.db = {}; @@ -1374,138 +1741,33 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we } }); -/** Configure blockui */ -if ($.blockUI) { - $.blockUI.defaults.baseZ = 1100; - $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">'; -} - -/** Configure default qweb */ -openerp.web._t = new openerp.web.TranslationDataBase().build_translation_function(); -/** - * Lazy translation function, only performs the translation when actually - * printed (e.g. inserted into a template) - * - * Useful when defining translatable strings in code evaluated before the - * translation database is loaded, as class attributes or at the top-level of - * an OpenERP Web module - * - * @param {String} s string to translate - * @returns {Object} lazy translation object - */ -openerp.web._lt = function (s) { - return {toString: function () { return openerp.web._t(s); }} -}; -openerp.web.qweb = new QWeb2.Engine(); -openerp.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined); -openerp.web.qweb.default_dict = { - '_' : _, - '_t' : openerp.web._t -}; -openerp.web.qweb.preprocess_node = function() { - // Note that 'this' is the Qweb Node - switch (this.node.nodeType) { - case 3: - case 4: - // Text and CDATAs - var translation = this.node.parentNode.attributes['t-translation']; - if (translation && translation.value === 'off') { - return; - } - var ts = _.str.trim(this.node.data); - if (ts.length === 0) { - return; - } - var tr = openerp.web._t(ts); - if (tr !== ts) { - this.node.data = tr; - } - break; - case 1: - // Element - var attr, attrs = ['label', 'title', 'alt']; - while (attr = attrs.pop()) { - if (this.attributes[attr]) { - this.attributes[attr] = openerp.web._t(this.attributes[attr]); - } - } - } -}; - /** - * A small utility function to check if a class implements correctly an interface, assuming that - * interface is simply specified using a dictionary containing methods and attributes with the - * correct type. It only performs the check when in debug mode and the only effect of an invalid - * check is messages in the console. + * @deprecated use :class:`instance.web.Widget` */ -openerp.web.check_interface = function(_class, _interface) { - if (! openerp.web.check_interface.debug) - return; - for (var member in _interface) { - if ( (typeof _class.prototype[member] != typeof _interface[member]) ) { - console.error("class failed to implement interface member '" + member + "'"); +instance.web.OldWidget = instance.web.Widget.extend({ + init: function(parent, element_id) { + this._super(parent); + this.element_id = element_id; + this.element_id = this.element_id || _.uniqueId('widget-'); + var tmp = document.getElementById(this.element_id); + this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName)); + }, + renderElement: function() { + var rendered = this.render(); + if (rendered) { + var elem = $(rendered); + this.$element.replaceWith(elem); + this.$element = elem; } + return this; + }, + render: function (additional) { + if (this.template) + return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {})); + return null; } -} -openerp.web.check_interface.debug = ($.deparam($.param.querystring()).debug != undefined); - -/** Jquery extentions */ -$.Mutex = (function() { - function Mutex() { - this.def = $.Deferred().resolve(); - } - Mutex.prototype.exec = function(action) { - var current = this.def; - var next = this.def = $.Deferred(); - return current.pipe(function() { - return $.when(action()).always(function() { - next.resolve(); - }); - }); - }; - return Mutex; -})(); - -/** Setup default connection */ -openerp.connection = new openerp.web.Connection(); -openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug; - - -$.async_when = function() { - var async = false; - var def = $.Deferred(); - $.when.apply($, arguments).then(function() { - var args = arguments; - var action = function() { - def.resolve.apply(def, args); - }; - if (async) - action(); - else - setTimeout(action, 0); - }, function() { - var args = arguments; - var action = function() { - def.reject.apply(def, args); - }; - if (async) - action(); - else - setTimeout(action, 0); - }); - async = true; - return def; -}; - -// special tweak for the web client -var old_async_when = $.async_when; -$.async_when = function() { - if (openerp.connection.synch) - return $.when.apply(this, arguments); - else - return old_async_when.apply(this, arguments); -}; +}); -}; +} // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js new file mode 100644 index 00000000000..86040302668 --- /dev/null +++ b/addons/web/static/src/js/coresetup.js @@ -0,0 +1,142 @@ +/*--------------------------------------------------------- + * OpenERP Web core + *--------------------------------------------------------*/ +var console; +if (!console) { + console = {log: function () {}}; +} +if (!console.debug) { + console.debug = console.log; +} + +openerp.web.coresetup = function(instance) { + +/** Configure default qweb */ +instance.web._t = new instance.web.TranslationDataBase().build_translation_function(); +/** + * Lazy translation function, only performs the translation when actually + * printed (e.g. inserted into a template) + * + * Useful when defining translatable strings in code evaluated before the + * translation database is loaded, as class attributes or at the top-level of + * an OpenERP Web module + * + * @param {String} s string to translate + * @returns {Object} lazy translation object + */ +instance.web._lt = function (s) { + return {toString: function () { return instance.web._t(s); }} +}; +instance.web.qweb = new QWeb2.Engine(); +instance.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined); +instance.web.qweb.default_dict = { + '_' : _, + '_t' : instance.web._t +}; +instance.web.qweb.preprocess_node = function() { + // Note that 'this' is the Qweb Node + switch (this.node.nodeType) { + case 3: + case 4: + // Text and CDATAs + var translation = this.node.parentNode.attributes['t-translation']; + if (translation && translation.value === 'off') { + return; + } + var ts = _.str.trim(this.node.data); + if (ts.length === 0) { + return; + } + var tr = instance.web._t(ts); + if (tr !== ts) { + this.node.data = tr; + } + break; + case 1: + // Element + var attr, attrs = ['label', 'title', 'alt']; + while (attr = attrs.pop()) { + if (this.attributes[attr]) { + this.attributes[attr] = instance.web._t(this.attributes[attr]); + } + } + } +}; + +/** Configure blockui */ +if ($.blockUI) { + $.blockUI.defaults.baseZ = 1100; + $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">'; +} + +/** Custom jQuery plugins */ +$.fn.getAttributes = function() { + var o = {}; + if (this.length) { + for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) { + attr = attrs.item(i) + o[attr.nodeName] = attr.nodeValue; + } + } + return o; +} + +/** Jquery extentions */ +$.Mutex = (function() { + function Mutex() { + this.def = $.Deferred().resolve(); + } + Mutex.prototype.exec = function(action) { + var current = this.def; + var next = this.def = $.Deferred(); + return current.pipe(function() { + return $.when(action()).always(function() { + next.resolve(); + }); + }); + }; + return Mutex; +})(); + +/** Setup default connection */ +instance.connection = new instance.web.Connection(); +instance.web.qweb.default_dict['__debug__'] = instance.connection.debug; + +$.async_when = function() { + var async = false; + var def = $.Deferred(); + $.when.apply($, arguments).then(function() { + var args = arguments; + var action = function() { + def.resolve.apply(def, args); + }; + if (async) + action(); + else + setTimeout(action, 0); + }, function() { + var args = arguments; + var action = function() { + def.reject.apply(def, args); + }; + if (async) + action(); + else + setTimeout(action, 0); + }); + async = true; + return def; +}; + +// special tweak for the web client +var old_async_when = $.async_when; +$.async_when = function() { + if (instance.connection.synch) + return $.when.apply(this, arguments); + else + return old_async_when.apply(this, arguments); +}; + +}; + +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index a29a9c5ad36..2dc66aaa135 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -1,5 +1,5 @@ -openerp.web.data = function(openerp) { +openerp.web.data = function(instance) { /** * Serializes the sort criterion array of a dataset into a form which can be @@ -8,7 +8,7 @@ openerp.web.data = function(openerp) { * @param {Array} criterion array of fields, from first to last criteria, prefixed with '-' for reverse sorting * @returns {String} SQL-like sorting string (``ORDER BY``) clause */ -openerp.web.serialize_sort = function (criterion) { +instance.web.serialize_sort = function (criterion) { return _.map(criterion, function (criteria) { if (criteria[0] === '-') { @@ -18,7 +18,7 @@ openerp.web.serialize_sort = function (criterion) { }).join(', '); }; -openerp.web.Query = openerp.web.Class.extend({ +instance.web.Query = instance.web.Class.extend({ init: function (model, fields) { this._model = model; this._fields = fields; @@ -30,7 +30,7 @@ openerp.web.Query = openerp.web.Class.extend({ }, clone: function (to_set) { to_set = to_set || {}; - var q = new openerp.web.Query(this._model, this._fields); + var q = new instance.web.Query(this._model, this._fields); q._context = this._context; q._filter = this._filter; q._limit = this._limit; @@ -41,11 +41,11 @@ openerp.web.Query = openerp.web.Class.extend({ if (!to_set.hasOwnProperty(key)) { continue; } switch(key) { case 'filter': - q._filter = new openerp.web.CompoundDomain( + q._filter = new instance.web.CompoundDomain( q._filter, to_set.filter); break; case 'context': - q._context = new openerp.web.CompoundContext( + q._context = new instance.web.CompoundContext( q._context, to_set.context); break; case 'limit': @@ -58,14 +58,14 @@ openerp.web.Query = openerp.web.Class.extend({ }, _execute: function () { var self = this; - return openerp.connection.rpc('/web/dataset/search_read', { + return instance.connection.rpc('/web/dataset/search_read', { model: this._model.name, fields: this._fields || false, domain: this._model.domain(this._filter), context: this._model.context(this._context), offset: this._offset, limit: this._limit, - sort: openerp.web.serialize_sort(this._order_by) + sort: instance.web.serialize_sort(this._order_by) }).pipe(function (results) { self._count = results.length; return results.records; @@ -127,10 +127,10 @@ openerp.web.Query = openerp.web.Class.extend({ context: this._model.context(this._context), offset: this._offset, limit: this._limit, - orderby: openerp.web.serialize_sort(this._order_by) || false + orderby: instance.web.serialize_sort(this._order_by) || false }).pipe(function (results) { return _(results).map(function (result) { - return new openerp.web.data.Group( + return new instance.web.data.Group( self._model.name, grouping[0], result); }); }); @@ -194,10 +194,10 @@ openerp.web.Query = openerp.web.Class.extend({ } }); -openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ +instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{ /** - * @constructs openerp.web.Model - * @extends openerp.web.Class + * @constructs instance.web.Model + * @extends instance.web.Class * * @param {String} model_name name of the OpenERP model this object is bound to * @param {Object} [context] @@ -233,7 +233,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ kwargs = args; args = []; } - return openerp.connection.rpc('/web/dataset/call_kw', { + return instance.connection.rpc('/web/dataset/call_kw', { model: this.name, method: method, args: args, @@ -247,7 +247,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ * @returns {openerp.web.Query} */ query: function (fields) { - return new openerp.web.Query(this, fields); + return new instance.web.Query(this, fields); }, /** * Executes a signal on the designated workflow, on the bound OpenERP model @@ -256,7 +256,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ * @param {String} signal signal to trigger on the workflow */ exec_workflow: function (id, signal) { - return openerp.connection.rpc('/web/dataset/exec_workflow', { + return instance.connection.rpc('/web/dataset/exec_workflow', { model: this.name, id: id, signal: signal @@ -270,7 +270,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ */ domain: function (domain) { if (!domain) { return this._domain; } - return new openerp.web.CompoundDomain( + return new instance.web.CompoundDomain( this._domain, domain); }, /** @@ -281,8 +281,8 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ * @returns The union of the user's context and the model's internal context, as well as the provided context if any. In that order. */ context: function (context) { - return new openerp.web.CompoundContext( - openerp.connection.user_context, this._context, context || {}); + return new instance.web.CompoundContext( + instance.connection.user_context, this._context, context || {}); }, /** * Button action caller, needs to perform cleanup if an action is returned @@ -292,7 +292,7 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ * FIXME: remove when evaluator integrated */ call_button: function (method, args) { - return openerp.connection.rpc('/web/dataset/call_button', { + return instance.connection.rpc('/web/dataset/call_button', { model: this.name, method: method, domain_id: null, @@ -302,12 +302,12 @@ openerp.web.Model = openerp.web.Class.extend(/** @lends openerp.web.Model# */{ }, }); -openerp.web.Traverser = openerp.web.Class.extend(/** @lends openerp.web.Traverser# */{ +instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{ /** - * @constructs openerp.web.Traverser - * @extends openerp.web.Class + * @constructs instance.web.Traverser + * @extends instance.web.Class * - * @param {openerp.web.Model} model instance this traverser is bound to + * @param {instance.web.Model} model instance this traverser is bound to */ init: function (model) { this._model = model; @@ -387,11 +387,11 @@ openerp.web.Traverser = openerp.web.Class.extend(/** @lends openerp.web.Traverse * * @namespace */ -openerp.web.data = { - Group: openerp.web.Class.extend(/** @lends openerp.web.data.Group# */{ +instance.web.data = { + Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{ /** - * @constructs openerp.web.data.Group - * @extends openerp.web.Class + * @constructs instance.web.data.Group + * @extends instance.web.Class */ init: function (model, grouping_field, read_group_group) { // In cases where group_by_no_leaf and no group_by, the result of @@ -411,7 +411,7 @@ openerp.web.data = { aggregates[key] = value || 0; }); - this.model = new openerp.web.Model( + this.model = new instance.web.Model( model, fixed_group.__context, fixed_group.__domain); var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0; @@ -439,7 +439,7 @@ openerp.web.data = { }) }; -openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{ +instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{ /** * Management interface between views and grouped collections of OpenERP * records. @@ -447,13 +447,13 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da * The root DataGroup is instantiated with the relevant information * (a session, a model, a domain, a context and a group_by sequence), the * domain and context may be empty. It is then interacted with via - * :js:func:`~openerp.web.DataGroup.list`, which is used to read the + * :js:func:`~instance.web.DataGroup.list`, which is used to read the * content of the current grouping level. * - * @constructs openerp.web.DataGroup - * @extends openerp.web.OldWidget + * @constructs instance.web.DataGroup + * @extends instance.web.OldWidget * - * @param {openerp.web.OldWidget} parent widget + * @param {instance.web.OldWidget} parent widget * @param {String} model name of the model managed by this DataGroup * @param {Array} domain search domain for this DataGroup * @param {Object} context context of the DataGroup's searches @@ -462,7 +462,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da */ init: function(parent, model, domain, context, group_by, level) { this._super(parent, null); - this.model = new openerp.web.Model(model, context, domain); + this.model = new instance.web.Model(model, context, domain); this.group_by = group_by; this.context = context; this.domain = domain; @@ -476,7 +476,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da .group_by(this.group_by)).then(function (groups) { if (!groups) { ifRecords(_.extend( - new openerp.web.DataSetSearch( + new instance.web.DataSetSearch( self, self.model.name, self.model.context(), self.model.domain()), @@ -487,7 +487,7 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da var child_context = _.extend( {}, self.model.context(), group.model.context()); return _.extend( - new openerp.web.DataGroup( + new instance.web.DataGroup( self, self.model.name, group.model.domain(), child_context, group.model._context.group_by, self.level + 1), @@ -504,16 +504,16 @@ openerp.web.DataGroup = openerp.web.OldWidget.extend( /** @lends openerp.web.Da }); } }); -openerp.web.ContainerDataGroup = openerp.web.DataGroup.extend({ }); -openerp.web.GrouplessDataGroup = openerp.web.DataGroup.extend({ }); +instance.web.ContainerDataGroup = instance.web.DataGroup.extend({ }); +instance.web.GrouplessDataGroup = instance.web.DataGroup.extend({ }); -openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ { +instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ { /** * A specialization of groupless data groups, relying on a single static * dataset as its records provider. * - * @constructs openerp.web.StaticDataGroup - * @extends openerp.web.GrouplessDataGroup + * @constructs instance.web.StaticDataGroup + * @extends instance.web.GrouplessDataGroup * @param {openep.web.DataSetStatic} dataset a static dataset backing the groups */ init: function (dataset) { @@ -524,13 +524,13 @@ openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends } }); -openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{ +instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{ /** * DateaManagement interface between views and the collection of selected * OpenERP records (represents the view's state?) * - * @constructs openerp.web.DataSet - * @extends openerp.web.OldWidget + * @constructs instance.web.DataSet + * @extends instance.web.OldWidget * * @param {String} model the OpenERP model this dataset will manage */ @@ -540,7 +540,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data this.context = context || {}; this.index = null; this._sort = []; - this._model = new openerp.web.Model(model, context); + this._model = new instance.web.Model(model, context); }, previous: function () { this.index -= 1; @@ -789,7 +789,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data */ sort: function (field, force_reverse) { if (!field) { - return openerp.web.serialize_sort(this._sort); + return instance.web.serialize_sort(this._sort); } var reverse = force_reverse || (this._sort[0] === field); this._sort.splice.apply( @@ -806,7 +806,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data this.ids = n_ids; }, }); -openerp.web.DataSetStatic = openerp.web.DataSet.extend({ +instance.web.DataSetStatic = instance.web.DataSet.extend({ init: function(parent, model, context, ids) { this._super(parent, model, context); // all local records @@ -836,10 +836,10 @@ openerp.web.DataSetStatic = openerp.web.DataSet.extend({ this.set_ids(_.without.apply(null, [this.ids].concat(ids))); } }); -openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{ +instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{ /** - * @constructs openerp.web.DataSetSearch - * @extends openerp.web.DataSet + * @constructs instance.web.DataSetSearch + * @extends instance.web.DataSet * * @param {Object} parent * @param {String} model @@ -851,7 +851,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D this.domain = domain || []; this._length = null; this.ids = []; - this._model = new openerp.web.Model(model, context, domain); + this._model = new instance.web.Model(model, context, domain); }, /** * Read a slice of the records represented by this DataSet, based on its @@ -906,7 +906,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D return this._super(); } }); -openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ +instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ virtual_id_prefix: "one2many_v_id_", debug_mode: true, init: function() { @@ -1071,9 +1071,9 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ this.on_change(); }, }); -openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/; +instance.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/; -openerp.web.ProxyDataSet = openerp.web.DataSetSearch.extend({ +instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({ init: function() { this._super.apply(this, arguments); this.create_function = null; @@ -1119,7 +1119,7 @@ openerp.web.ProxyDataSet = openerp.web.DataSetSearch.extend({ on_unlink: function(ids) {} }); -openerp.web.CompoundContext = openerp.web.Class.extend({ +instance.web.CompoundContext = instance.web.Class.extend({ init: function () { this.__ref = "compound_context"; this.__contexts = []; @@ -1142,7 +1142,7 @@ openerp.web.CompoundContext = openerp.web.Class.extend({ } }); -openerp.web.CompoundDomain = openerp.web.Class.extend({ +instance.web.CompoundDomain = instance.web.Class.extend({ init: function () { this.__ref = "compound_domain"; this.__domains = []; @@ -1165,10 +1165,10 @@ openerp.web.CompoundDomain = openerp.web.Class.extend({ } }); -openerp.web.DropMisordered = openerp.web.Class.extend(/** @lends openerp.web.DropMisordered# */{ +instance.web.DropMisordered = instance.web.Class.extend(/** @lends openerp.web.DropMisordered# */{ /** - * @constructs openerp.web.DropMisordered - * @extends openerp.web.Class + * @constructs instance.web.DropMisordered + * @extends instance.web.Class * * @param {Boolean} [failMisordered=false] whether mis-ordered responses should be failed or just ignored */ diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 3d15a4e3607..ebe82ddb4b4 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -1,14 +1,14 @@ -openerp.web.data_export = function(openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t; -openerp.web.DataExport = openerp.web.Dialog.extend({ +openerp.web.data_export = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; +instance.web.DataExport = instance.web.Dialog.extend({ template: 'ExportTreeView', dialog_title: {toString: function () { return _t("Export Data"); }}, init: function(parent, dataset) { this._super(parent); this.records = {}; this.dataset = dataset; - this.exports = new openerp.web.DataSetSearch( + this.exports = new instance.web.DataSetSearch( this, 'ir.exports', this.dataset.get_context()); }, start: function() { diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 3054a7784c4..30f9323f723 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -1,6 +1,6 @@ -openerp.web.data_import = function(openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t; +openerp.web.data_import = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; /** * Safari does not deal well at all with raw JSON data being returned. As a * result, we're going to cheat by using a pseudo-jsonp: instead of getting @@ -29,7 +29,7 @@ function jsonp(form, attributes, callback) { $(form).ajaxSubmit(attributes); } -openerp.web.DataImport = openerp.web.Dialog.extend({ +instance.web.DataImport = instance.web.Dialog.extend({ template: 'ImportDataView', dialog_title: {toString: function () { return _t("Import Data"); }}, init: function(parent, dataset){ @@ -79,10 +79,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.$element.delegate('fieldset legend', 'click', function() { $(this).parent().toggleClass('oe-closed'); }); - this.ready.push(new openerp.web.DataSet(this, this.model).call( + this.ready.push(new instance.web.DataSet(this, this.model).call( 'fields_get', [], function (fields) { self.graft_fields(fields); - self.ready.push(new openerp.web.DataSet(self, self.model) + self.ready.push(new instance.web.DataSet(self, self.model) .default_get(_.pluck(self.fields, 'id')).then(function (fields) { _.each(fields, function(val, key) { if (val) { @@ -140,7 +140,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ f.fields = []; // only fetch sub-fields to a depth of 2 levels if (level < 2) { - self.ready.push(new openerp.web.DataSet(self, field.relation).call( + self.ready.push(new instance.web.DataSet(self, field.relation).call( 'fields_get', [], function (fields) { self.graft_fields(fields, f, level+1); })); @@ -151,7 +151,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }); }, toggle_import_button: function (newstate) { - openerp.web.dialog(this.$element, 'widget') + instance.web.dialog(this.$element, 'widget') .find('.oe-dialog-import-button') .button('option', 'disabled', !newstate); }, diff --git a/addons/web/static/src/js/dates.js b/addons/web/static/src/js/dates.js index d61223113f8..3db48c8412a 100644 --- a/addons/web/static/src/js/dates.js +++ b/addons/web/static/src/js/dates.js @@ -1,5 +1,5 @@ -openerp.web.dates = function(openerp) { +openerp.web.dates = function(instance) { /** * Converts a string to a Date javascript object using OpenERP's @@ -11,7 +11,7 @@ openerp.web.dates = function(openerp) { * @param {String} str A string representing a datetime. * @returns {Date} */ -openerp.web.str_to_datetime = function(str) { +instance.web.str_to_datetime = function(str) { if(!str) { return str; } @@ -38,7 +38,7 @@ openerp.web.str_to_datetime = function(str) { * @param {String} str A string representing a date. * @returns {Date} */ -openerp.web.str_to_date = function(str) { +instance.web.str_to_date = function(str) { if(!str) { return str; } @@ -65,7 +65,7 @@ openerp.web.str_to_date = function(str) { * @param {String} str A string representing a time. * @returns {Date} */ -openerp.web.str_to_time = function(str) { +instance.web.str_to_time = function(str) { if(!str) { return str; } @@ -104,7 +104,7 @@ var zpad = function(str, size) { * @param {Date} obj * @returns {String} A string representing a datetime. */ -openerp.web.datetime_to_str = function(obj) { +instance.web.datetime_to_str = function(obj) { if (!obj) { return false; } @@ -124,7 +124,7 @@ openerp.web.datetime_to_str = function(obj) { * @param {Date} obj * @returns {String} A string representing a date. */ -openerp.web.date_to_str = function(obj) { +instance.web.date_to_str = function(obj) { if (!obj) { return false; } @@ -143,7 +143,7 @@ openerp.web.date_to_str = function(obj) { * @param {Date} obj * @returns {String} A string representing a time. */ -openerp.web.time_to_str = function(obj) { +instance.web.time_to_str = function(obj) { if (!obj) { return false; } diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 53863e63e97..640ba4d5a93 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -1,6 +1,6 @@ -openerp.web.formats = function(openerp) { -var _t = openerp.web._t; +openerp.web.formats = function(instance) { +var _t = instance.web._t; /** * Intersperses ``separator`` in ``str`` at the positions indicated by @@ -23,7 +23,7 @@ var _t = openerp.web._t; * @param {String} separator * @returns {String} */ -openerp.web.intersperse = function (str, indices, separator) { +instance.web.intersperse = function (str, indices, separator) { separator = separator || ''; var result = [], last = str.length; @@ -55,10 +55,10 @@ openerp.web.intersperse = function (str, indices, separator) { * @param {String} num * @returns {String} */ -openerp.web.insert_thousand_seps = function (num) { +instance.web.insert_thousand_seps = function (num) { var negative = num[0] === '-'; num = (negative ? num.slice(1) : num); - return (negative ? '-' : '') + openerp.web.intersperse( + return (negative ? '-' : '') + instance.web.intersperse( num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep); }; @@ -69,7 +69,7 @@ openerp.web.insert_thousand_seps = function (num) { * * @param {String} value original format */ -openerp.web.strip_raw_chars = function (value) { +instance.web.strip_raw_chars = function (value) { var isletter = /[a-zA-Z]/, output = []; for(var index=0; index < value.length; ++index) { var character = value[index]; @@ -81,7 +81,7 @@ openerp.web.strip_raw_chars = function (value) { return output.join(''); }; var normalize_format = function (format) { - return Date.normalizeFormat(openerp.web.strip_raw_chars(format)); + return Date.normalizeFormat(instance.web.strip_raw_chars(format)); }; /** * Formats a single atomic value based on a field descriptor @@ -93,7 +93,7 @@ var normalize_format = function (format) { * @param {Object} [descriptor.digits] used for the formatting of floats * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty */ -openerp.web.format_value = function (value, descriptor, value_if_empty) { +instance.web.format_value = function (value, descriptor, value_if_empty) { // If NaN value, display as with a `false` (empty cell) if (typeof value === 'number' && isNaN(value)) { value = false; @@ -111,16 +111,16 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { return value_if_empty === undefined ? '' : value_if_empty; } var l10n = _t.database.parameters; - switch (descriptor.widget || descriptor.type) { + switch (descriptor.type || (descriptor.field && descriptor.field.type)) { case 'id': return value.toString(); case 'integer': - return openerp.web.insert_thousand_seps( + return instance.web.insert_thousand_seps( _.str.sprintf('%d', value)); case 'float': var precision = descriptor.digits ? descriptor.digits[1] : 2; var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.'); - formatted[0] = openerp.web.insert_thousand_seps(formatted[0]); + formatted[0] = instance.web.insert_thousand_seps(formatted[0]); return formatted.join(l10n.decimal_point); case 'float_time': var pattern = '%02d:%02d'; @@ -136,17 +136,17 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { return value[1]; case 'datetime': if (typeof(value) == "string") - value = openerp.web.auto_str_to_date(value); + value = instance.web.auto_str_to_date(value); return value.toString(normalize_format(l10n.date_format) + ' ' + normalize_format(l10n.time_format)); case 'date': if (typeof(value) == "string") - value = openerp.web.auto_str_to_date(value); + value = instance.web.auto_str_to_date(value); return value.toString(normalize_format(l10n.date_format)); case 'time': if (typeof(value) == "string") - value = openerp.web.auto_str_to_date(value); + value = instance.web.auto_str_to_date(value); return value.toString(normalize_format(l10n.time_format)); case 'selection': case 'statusbar': // Each choice is [value, label] @@ -163,7 +163,7 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { } }; -openerp.web.parse_value = function (value, descriptor, value_if_empty) { +instance.web.parse_value = function (value, descriptor, value_if_empty) { var date_pattern = normalize_format(_t.database.parameters.date_format), time_pattern = normalize_format(_t.database.parameters.time_format); switch (value) { @@ -171,12 +171,12 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { case "": return value_if_empty === undefined ? false : value_if_empty; } - switch (descriptor.widget || descriptor.type) { + switch (descriptor.type || (descriptor.field && descriptor.field.type)) { case 'integer': var tmp; do { tmp = value; - value = value.replace(openerp.web._t.database.parameters.thousands_sep, ""); + value = value.replace(instance.web._t.database.parameters.thousands_sep, ""); } while(tmp !== value); tmp = Number(value); if (isNaN(tmp)) @@ -190,9 +190,9 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { var tmp2 = value; do { tmp = tmp2; - tmp2 = tmp.replace(openerp.web._t.database.parameters.thousands_sep, ""); + tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, ""); } while(tmp !== tmp2); - var reformatted_value = tmp.replace(openerp.web._t.database.parameters.decimal_point, "."); + var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, "."); var parsed = Number(reformatted_value); if (isNaN(parsed)) throw new Error(value + " is not a correct float"); @@ -205,62 +205,62 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { } var float_time_pair = value.split(":"); if (float_time_pair.length != 2) - return factor * openerp.web.parse_value(value, {type: "float"}); - var hours = openerp.web.parse_value(float_time_pair[0], {type: "integer"}); - var minutes = openerp.web.parse_value(float_time_pair[1], {type: "integer"}); + return factor * instance.web.parse_value(value, {type: "float"}); + var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"}); + var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"}); return factor * (hours + (minutes / 60)); case 'progressbar': - return openerp.web.parse_value(value, {type: "float"}); + return instance.web.parse_value(value, {type: "float"}); case 'datetime': var datetime = Date.parseExact( value, (date_pattern + ' ' + time_pattern)); if (datetime !== null) - return openerp.web.datetime_to_str(datetime); + return instance.web.datetime_to_str(datetime); datetime = Date.parse(value); if (datetime !== null) - return openerp.web.datetime_to_str(datetime); + return instance.web.datetime_to_str(datetime); throw new Error(value + " is not a valid datetime"); case 'date': var date = Date.parseExact(value, date_pattern); if (date !== null) - return openerp.web.date_to_str(date); + return instance.web.date_to_str(date); date = Date.parse(value); if (date !== null) - return openerp.web.date_to_str(date); + return instance.web.date_to_str(date); throw new Error(value + " is not a valid date"); case 'time': var time = Date.parseExact(value, time_pattern); if (time !== null) - return openerp.web.time_to_str(time); + return instance.web.time_to_str(time); time = Date.parse(value); if (time !== null) - return openerp.web.time_to_str(time); + return instance.web.time_to_str(time); throw new Error(value + " is not a valid time"); } return value; }; -openerp.web.auto_str_to_date = function(value, type) { +instance.web.auto_str_to_date = function(value, type) { try { - return openerp.web.str_to_datetime(value); + return instance.web.str_to_datetime(value); } catch(e) {} try { - return openerp.web.str_to_date(value); + return instance.web.str_to_date(value); } catch(e) {} try { - return openerp.web.str_to_time(value); + return instance.web.str_to_time(value); } catch(e) {} throw new Error("'" + value + "' is not a valid date, datetime nor time"); }; -openerp.web.auto_date_to_str = function(value, type) { +instance.web.auto_date_to_str = function(value, type) { switch(type) { case 'datetime': - return openerp.web.datetime_to_str(value); + return instance.web.datetime_to_str(value); case 'date': - return openerp.web.date_to_str(value); + return instance.web.date_to_str(value); case 'time': - return openerp.web.time_to_str(value); + return instance.web.time_to_str(value); default: throw new Error(type + " is not convertible to date, datetime nor time"); } @@ -290,7 +290,7 @@ openerp.web.auto_date_to_str = function(value, type) { * @param {Number} [options.id] current record's id * */ -openerp.web.format_cell = function (row_data, column, options) { +instance.web.format_cell = function (row_data, column, options) { options = options || {}; var attrs = {}; if (options.process_modifiers !== false) { @@ -303,9 +303,9 @@ openerp.web.format_cell = function (row_data, column, options) { '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' + '</button>', { title: column.string || '', - additional_attributes: isNaN(row_data["id"].value) && openerp.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ? + additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ? 'disabled="disabled" class="oe-listview-button-disabled"' : '', - prefix: openerp.connection.prefix, + prefix: instance.connection.prefix, icon: column.icon, alt: column.string || '' }); @@ -320,11 +320,11 @@ openerp.web.format_cell = function (row_data, column, options) { row_data[column.id].value ? 'checked="checked"' : ''); case "binary": var text = _t("Download"), - download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', openerp.connection.session_id, options.model, column.id, options.id); + download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id); if (column.filename) { download_url += '&filename_field=' + column.filename; if (row_data[column.filename]) { - text = _.str.sprintf(_t("Download \"%s\""), openerp.web.format_value( + text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value( row_data[column.filename].value, {type: 'char'})); } } @@ -340,7 +340,7 @@ openerp.web.format_cell = function (row_data, column, options) { }); } - return _.escape(openerp.web.format_value( + return _.escape(instance.web.format_value( row_data[column.id].value, column, options.value_if_empty)); } diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 773c10382f6..c2df866b5c6 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1,13 +1,80 @@ -openerp.web.search = function(openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t, - _lt = openerp.web._lt; +openerp.web.search = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t, + _lt = instance.web._lt; +_.mixin({ + sum: function (obj) { return _.reduce(obj, function (a, b) { return a + b; }, 0); } +}); + +// Have SearchBox optionally use callback function to produce inputs and facets +// (views) set on callbacks.make_facet and callbacks.make_input keys when +// initializing VisualSearch +var SearchBox_renderFacet = function (facet, position) { + var view = new (this.app.options.callbacks['make_facet'] || VS.ui.SearchFacet)({ + app : this.app, + model : facet, + order : position + }); -openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.SearchView# */{ - template: "EmptyComponent", + // Input first, facet second. + this.renderSearchInput(); + this.facetViews.push(view); + this.$('.VS-search-inner').children().eq(position*2).after(view.render().el); + + view.calculateSize(); + _.defer(_.bind(view.calculateSize, view)); + + return view; + }; // warning: will not match +// Ensure we're replacing the function we think +if (SearchBox_renderFacet.toString() !== VS.ui.SearchBox.prototype.renderFacet.toString().replace(/(VS\.ui\.SearchFacet)/, "(this.app.options.callbacks['make_facet'] || $1)")) { + throw new Error( + "Trying to replace wrong version of VS.ui.SearchBox#renderFacet. " + + "Please fix replacement."); +} +var SearchBox_renderSearchInput = function () { + var input = new (this.app.options.callbacks['make_input'] || VS.ui.SearchInput)({position: this.inputViews.length, app: this.app}); + this.$('.VS-search-inner').append(input.render().el); + this.inputViews.push(input); + }; +// Ensure we're replacing the function we think +if (SearchBox_renderSearchInput.toString() !== VS.ui.SearchBox.prototype.renderSearchInput.toString().replace(/(VS\.ui\.SearchInput)/, "(this.app.options.callbacks['make_input'] || $1)")) { + throw new Error( + "Trying to replace wrong version of VS.ui.SearchBox#renderSearchInput. " + + "Please fix replacement."); +} +var SearchBox_searchEvent = function (e) { + var query = null; + this.renderFacets(); + this.focusSearch(e); + this.app.options.callbacks.search(query, this.app.searchQuery); + }; +if (SearchBox_searchEvent.toString() !== VS.ui.SearchBox.prototype.searchEvent.toString().replace( + /this\.value\(\);\n[ ]{4}this\.focusSearch\(e\);\n[ ]{4}this\.value\(query\)/, + 'null;\n this.renderFacets();\n this.focusSearch(e)')) { + throw new Error( + "Trying to replace wrong version of VS.ui.SearchBox#searchEvent. " + + "Please fix replacement."); +} +_.extend(VS.ui.SearchBox.prototype, { + renderFacet: SearchBox_renderFacet, + renderSearchInput: SearchBox_renderSearchInput, + searchEvent: SearchBox_searchEvent +}); +_.extend(VS.model.SearchFacet.prototype, { + value: function () { + if (this.has('json')) { + return this.get('json'); + } + return this.get('value'); + } +}); + +instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.SearchView# */{ + template: "SearchView", /** - * @constructs openerp.web.SearchView - * @extends openerp.web.OldWidget + * @constructs instance.web.SearchView + * @extends instance.web.OldWidget * * @param parent * @param element_id @@ -25,9 +92,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea this.has_defaults = !_.isEmpty(this.defaults); this.inputs = []; - this.enabled_filters = []; - - this.has_focus = false; + this.controls = {}; this.hidden = !!hidden; this.headless = this.hidden && !this.has_defaults; @@ -37,20 +102,63 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea this.ready = $.Deferred(); }, start: function() { - this._super(); + var self = this; + var p = this._super(); + + this.setup_global_completion(); + this.vs = VS.init({ + container: this.$element, + query: '', + callbacks: { + make_facet: this.proxy('make_visualsearch_facet'), + make_input: this.proxy('make_visualsearch_input'), + search: function (query, searchCollection) { + self.do_search(); + }, + facetMatches: function (callback) { + }, + valueMatches : function(facet, searchTerm, callback) { + } + } + }); + + var search = function () { self.vs.searchBox.searchEvent({}); }; + // searchQuery operations + this.vs.searchQuery + .off('add').on('add', search) + .off('change').on('change', search) + .off('reset').on('reset', search) + .off('remove').on('remove', function (record, collection, options) { + if (options['trigger_search']) { + search(); + } + }); + if (this.hidden) { this.$element.hide(); } if (this.headless) { this.ready.resolve(); } else { - this.rpc("/web/searchview/load", { + var load_view = this.rpc("/web/searchview/load", { model: this.model, view_id: this.view_id, - context: this.dataset.get_context() - }, this.on_loaded); + context: this.dataset.get_context() }); + // FIXME: local eval of domain and context to get rid of special endpoint + var filters = this.rpc('/web/searchview/get_filters', { + model: this.model + }).then(function (filters) { self.custom_filters = filters; }); + + $.when(load_view, filters) + .pipe(function (load) { return load[0]; }) + .then(this.on_loaded); } - return this.ready.promise(); + + this.$element.on('click', '.oe_vs_unfold_drawer', function () { + self.$element.toggleClass('oe_searchview_open_drawer'); + }); + + return $.when(p, this.ready); }, show: function () { this.$element.show(); @@ -58,61 +166,227 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea hide: function () { this.$element.hide(); }, + + /** + * Sets up thingie where all the mess is put? + */ + setup_stuff_drawer: function () { + var self = this; + $('<div class="oe_vs_unfold_drawer">').appendTo(this.$element.find('.VS-search-box')); + var $drawer = $('<div class="oe_searchview_drawer">').appendTo(this.$element); + var $filters = $('<div class="oe_searchview_filters">').appendTo($drawer); + + var running_count = 0; + // get total filters count + var is_group = function (i) { return i instanceof instance.web.search.FilterGroup; }; + var filters_count = _(this.controls).chain() + .flatten() + .filter(is_group) + .map(function (i) { return i.filters.length; }) + .sum() + .value(); + + var col1 = [], col2 = _(this.controls).map(function (inputs, group) { + var filters = _(inputs).filter(is_group); + return { + name: group === 'null' ? _t("Filters") : group, + filters: filters, + length: _(filters).chain().map(function (i) { + return i.filters.length; }).sum().value() + }; + }); + + while (col2.length) { + // col1 + group should be smaller than col2 + group + if ((running_count + col2[0].length) <= (filters_count - running_count)) { + running_count += col2[0].length; + col1.push(col2.shift()); + } else { + break; + } + } + + // Create a Custom Filter FilterGroup for each custom filter read from + // the db, add all of this as a group in the smallest column + [].push.call(col1.length <= col2.length ? col1 : col2, { + name: _t("Custom Filters"), + filters: _.map(this.custom_filters, function (filter) { + // FIXME: handling of ``disabled`` being set + var f = new instance.web.search.Filter({attrs: { + string: filter.name, + context: filter.context, + domain: filter.domain + }}, self); + return new instance.web.search.FilterGroup([f], self); + }), + length: 3 + }); + + return $.when( + this.render_column(col1, $('<div>').appendTo($filters)), + this.render_column(col2, $('<div>').appendTo($filters)), + (new instance.web.search.Advanced(this).appendTo($drawer))); + }, + render_column: function (column, $el) { + return $.when.apply(null, _(column).map(function (group) { + $('<h3>').text(group.name).appendTo($el); + return $.when.apply(null, + _(group.filters).invoke('appendTo', $el)); + })); + }, + /** + * Sets up search view's view-wide auto-completion widget + */ + setup_global_completion: function () { + // Prevent keydown from within a facet's input from reaching the + // auto-completion widget and opening the completion list + this.$element.on('keydown', '.search_facet input', function (e) { + e.stopImmediatePropagation(); + }); + + this.$element.autocomplete({ + source: this.proxy('complete_global_search'), + select: this.proxy('select_completion'), + focus: function (e) { e.preventDefault(); }, + html: true, + minLength: 0, + delay: 0 + }).data('autocomplete')._renderItem = function (ul, item) { + // item of completion list + var $item = $( "<li></li>" ) + .data( "item.autocomplete", item ) + .appendTo( ul ); + + if (item.value !== undefined) { + // regular completion item + return $item.append( + (item.label) + ? $('<a>').html(item.label) + : $('<a>').text(item.value)); + } + return $item.text(item.category) + .css({ + borderTop: '1px solid #cccccc', + margin: 0, + padding: 0, + zoom: 1, + 'float': 'left', + clear: 'left', + width: '100%' + }); + } + }, + /** + * Provide auto-completion result for req.term (an array to `resp`) + * + * @param {Object} req request to complete + * @param {String} req.term searched term to complete + * @param {Function} resp response callback + */ + complete_global_search: function (req, resp) { + $.when.apply(null, _(this.inputs).chain() + .invoke('complete', req.term) + .value()).then(function () { + resp(_(_(arguments).compact()).flatten(true)); + }); + }, + + /** + * Action to perform in case of selection: create a facet (model) + * and add it to the search collection + * + * @param {Object} e selection event, preventDefault to avoid setting value on object + * @param {Object} ui selection information + * @param {Object} ui.item selected completion item + */ + select_completion: function (e, ui) { + e.preventDefault(); + this.vs.searchQuery.add(new VS.model.SearchFacet(_.extend( + {app: this.vs}, ui.item))); + this.vs.searchBox.searchEvent({}); + }, + + /** + * Builds the right SearchFacet view based on the facet object to render + * (e.g. readonly facets for filters) + * + * @param {Object} options + * @param {VS.model.SearchFacet} options.model facet object to render + */ + make_visualsearch_facet: function (options) { + return new instance.web.search.FilterGroupFacet(options); + +// if (options.model.get('field') instanceof instance.web.search.FilterGroup) { +// return new instance.web.search.FilterGroupFacet(options); +// } +// return new VS.ui.SearchFacet(options); + }, + /** + * Proxies searches on a SearchInput to the search view's global completion + * + * Also disables SearchInput.autocomplete#_move so search view's + * autocomplete can get the corresponding events, or something. + * + * @param options + */ + make_visualsearch_input: function (options) { + var self = this, input = new VS.ui.SearchInput(options); + input.setupAutocomplete = function () { + _.extend(this.box.autocomplete({ + minLength: 1, + delay: 0, + search: function () { + self.$element.autocomplete('search', input.box.val()); + return false; + } + }).data('autocomplete'), { + _move: function () {}, + close: function () { self.$element.autocomplete('close'); } + }); + }; + return input; + }, + /** * Builds a list of widget rows (each row is an array of widgets) * * @param {Array} items a list of nodes to convert to widgets * @param {Object} fields a mapping of field names to (ORM) field attributes - * @returns Array + * @param {String} [group_name] name of the group to put the new controls in */ - make_widgets: function (items, fields) { - var rows = [], - row = []; - rows.push(row); + make_widgets: function (items, fields, group_name) { + group_name = group_name || null; + if (!(group_name in this.controls)) { + this.controls[group_name] = []; + } + var self = this, group = this.controls[group_name]; var filters = []; _.each(items, function (item) { if (filters.length && item.tag !== 'filter') { - row.push( - new openerp.web.search.FilterGroup( - filters, this)); + group.push(new instance.web.search.FilterGroup(filters, this)); filters = []; } - if (item.tag === 'newline') { - row = []; - rows.push(row); - } else if (item.tag === 'filter') { - if (!this.has_focus) { - item.attrs.default_focus = '1'; - this.has_focus = true; - } - filters.push( - new openerp.web.search.Filter( - item, this)); - } else if (item.tag === 'separator') { - // a separator is a no-op - } else { - if (item.tag === 'group') { - // TODO: group and field should be fetched from registries, maybe even filters - row.push( - new openerp.web.search.Group( - item, this, fields)); - } else if (item.tag === 'field') { - if (!this.has_focus) { - item.attrs.default_focus = '1'; - this.has_focus = true; - } - row.push( - this.make_field( - item, fields[item['attrs'].name])); - } + switch (item.tag) { + case 'separator': case 'newline': + break; + case 'filter': + filters.push(new instance.web.search.Filter(item, this)); + break; + case 'group': + self.make_widgets(item.children, fields, item.attrs.string); + break; + case 'field': + group.push(this.make_field(item, fields[item['attrs'].name])); + // filters + self.make_widgets(item.children, fields, group_name); + break; } }, this); + if (filters.length) { - row.push(new openerp.web.search.FilterGroup(filters, this)); + group.push(new instance.web.search.FilterGroup(filters, this)); } - - return rows; }, /** * Creates a field for the provided field descriptor item (which comes @@ -120,18 +394,13 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea * * @param {Object} item fields_view_get node for the field * @param {Object} field fields_get result for the field - * @returns openerp.web.search.Field + * @returns instance.web.search.Field */ make_field: function (item, field) { - try { - return new (openerp.web.search.fields.get_any( - [item.attrs.widget, field.type])) - (item, field, this); - } catch (e) { - if (! e instanceof openerp.web.KeyNotFound) { - throw e; - } - // KeyNotFound means unknown field type + var obj = instance.web.search.fields.get_any( [item.attrs.widget, field.type]); + if(obj) { + return new (obj) (item, field, this); + } else { console.group('Unknown field type ' + field.type); console.error('View node', item); console.info('View field', field); @@ -141,6 +410,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea } }, on_loaded: function(data) { + var self = this; this.fields_view = data.fields_view; if (data.fields_view.type !== 'search' || data.fields_view.arch.tag !== 'search') { @@ -148,52 +418,20 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea "Got non-search view after asking for a search view: type %s, arch root %s", data.fields_view.type, data.fields_view.arch.tag)); } - var self = this, - lines = this.make_widgets( - data.fields_view['arch'].children, - data.fields_view.fields); - - // for extended search view - var ext = new openerp.web.search.ExtendedSearch(this, this.model); - lines.push([ext]); - this.extended_search = ext; - - var render = QWeb.render("SearchView", { - 'view': data.fields_view['arch'], - 'lines': lines, - 'defaults': this.defaults - }); - this.$element.html(render); + this.make_widgets( + data.fields_view['arch'].children, + data.fields_view.fields); - var f = this.$element.find('form'); - this.$element.find('form') - .submit(this.do_search) - .bind('reset', this.do_clear); - // start() all the widgets - var widget_starts = _(lines).chain().flatten().map(function (widget) { - return widget.start(); - }).value(); - - $.when.apply(null, widget_starts).then(function () { - self.ready.resolve(); - }); - - this.reload_managed_filters(); - }, - reload_managed_filters: function() { - var self = this; - return this.rpc('/web/searchview/get_filters', { - model: this.dataset.model - }).then(function(result) { - self.managed_filters = result; - var filters = self.$element.find(".oe_search-view-filters-management"); - filters.html(QWeb.render("SearchView.managed-filters", { - filters: result, - disabled_filter_message: _t('Filter disabled due to invalid syntax') - })); - filters.change(self.on_filters_management); - }); + // load defaults + return $.when( + this.setup_stuff_drawer(), + $.when.apply(null, _(this.inputs).invoke('facet_for_defaults', this.defaults)) + .then(function () { + self.vs.searchQuery.reset(_(arguments).compact(), {silent: true}); + self.vs.searchBox.renderFacets(); + })) + .then(function () { self.ready.resolve(); }) }, /** * Handle event when the user make a selection in the filters management select box. @@ -222,11 +460,11 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea break; case 'save_filter': var data = this.build_search_data(); - var context = new openerp.web.CompoundContext(); + var context = new instance.web.CompoundContext(); _.each(data.contexts, function(x) { context.add(x); }); - var domain = new openerp.web.CompoundDomain(); + var domain = new instance.web.CompoundDomain(); _.each(data.domains, function(x) { domain.add(x); }); @@ -234,7 +472,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea context.add({"group_by": groupbys}); var dial_html = QWeb.render("SearchView.managed-filters.add"); var $dial = $(dial_html); - openerp.web.dialog($dial, { + instance.web.dialog($dial, { modal: true, title: _t("Filter Entry"), buttons: [ @@ -287,13 +525,13 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea on_add_to_dashboard: function() { this.$element.find(".oe_search-view-filters-management")[0].selectedIndex = 0; var self = this, - menu = openerp.webclient.menu, + menu = instance.webclient.menu, $dialog = $(QWeb.render("SearchView.add_to_dashboard", { dashboards : menu.data.data.children, selected_menu_id : menu.$element.find('a.active').data('menu') })); $dialog.find('input').val(this.fields_view.name); - openerp.web.dialog($dialog, { + instance.web.dialog($dialog, { modal: true, title: _t("Add to Dashboard"), buttons: [ @@ -305,8 +543,8 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea var menu_id = $(this).find("select").val(), title = $(this).find("input").val(), data = self.build_search_data(), - context = new openerp.web.CompoundContext(), - domain = new openerp.web.CompoundDomain(); + context = new instance.web.CompoundContext(), + domain = new instance.web.CompoundDomain(); _.each(data.contexts, function(x) { context.add(x); }); @@ -335,47 +573,33 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea * Performs the search view collection of widget data. * * If the collection went well (all fields are valid), then triggers - * :js:func:`openerp.web.SearchView.on_search`. + * :js:func:`instance.web.SearchView.on_search`. * * If at least one field failed its validation, triggers - * :js:func:`openerp.web.SearchView.on_invalid` instead. + * :js:func:`instance.web.SearchView.on_invalid` instead. * * @param e jQuery event object coming from the "Search" button */ - do_search: function (e) { - if (this.headless && !this.has_defaults) { - return this.on_search([], [], []); - } - - if (e && e.preventDefault) { e.preventDefault(); } - - var data = this.build_search_data(); + do_search: function () { + var domains = [], contexts = [], groupbys = [], errors = []; - if (data.errors.length) { - this.on_invalid(data.errors); - return; - } - - this.on_search(data.domains, data.contexts, data.groupbys); - }, - build_search_data: function() { - var domains = [], - contexts = [], - errors = []; - - _.each(this.inputs, function (input) { + this.vs.searchQuery.each(function (facet) { + var field = facet.get('field'); try { - var domain = input.get_domain(); + var domain = field.get_domain(facet); if (domain) { domains.push(domain); } - - var context = input.get_context(); + var context = field.get_context(facet); if (context) { contexts.push(context); } + var group_by = field.get_groupby(facet); + if (group_by) { + groupbys.push.apply(groupbys, group_by); + } } catch (e) { - if (e instanceof openerp.web.search.Invalid) { + if (e instanceof instance.web.search.Invalid) { errors.push(e); } else { throw e; @@ -383,23 +607,11 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea } }); - // TODO: do we need to handle *fields* with group_by in their context? - var groupbys = _(this.enabled_filters) - .chain() - .map(function (filter) { return filter.get_context();}) - .compact() - .value(); - - if (this.filter_data.contexts) { - contexts = this.filter_data.contexts.concat(contexts) - } - if (this.filter_data.domains) { - domains = this.filter_data.domains.concat(domains); - } - if (this.filter_data.groupbys) { - groupbys = this.filter_data.groupbys.concat(groupbys); + if (!_.isEmpty(errors)) { + this.on_invalid(errors); + return; } - return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys}; + return this.on_search(domains, contexts, groupbys); }, /** * Triggered after the SearchView has collected all relevant domains and @@ -432,84 +644,68 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea */ on_invalid: function (errors) { this.do_notify(_t("Invalid Search"), _t("triggered from search view")); - }, - /** - * @param {Boolean} [reload_view=true] - */ - do_clear: function (reload_view) { - this.filter_data = {}; - this.$element.find(".oe_search-view-filters-management").val(''); + } +}); + +/** @namespace */ +instance.web.search = {}; - this.$element.find('.filter_label, .filter_icon').removeClass('enabled'); - this.enabled_filters.splice(0); - var string = $('a.searchview_group_string'); - _.each(string, function(str){ - $(str).closest('div.searchview_group').removeClass("expanded").addClass('folded'); - }); - this.$element.find('table:last').hide(); +instance.web.search.FilterGroupFacet = VS.ui.SearchFacet.extend({ + events: _.extend({ + 'click': 'selectFacet' + }, VS.ui.SearchFacet.prototype.events), - $('.searchview_extended_groups_list').empty(); - return $.async_when.apply( - null, _(this.inputs).invoke('clear')).pipe( - reload_view !== false ? this.on_clear : null); + render: function () { + this.setMode('not', 'editing'); + this.setMode('not', 'selected'); + + var value = this.model.get('value'); + this.$el.html(QWeb.render('SearchView.filters.facet', { + facet: this.model + })); + // virtual input so SearchFacet code has something to play with + this.box = $('<input>').val(value); + + return this; }, - /** - * Triggered when the search view gets cleared - * - * @event - */ - on_clear: function () { - this.do_search(); + enableEdit: function () { + this.selectFacet() }, - /** - * Called by a filter propagating its state changes - * - * @param {openerp.web.search.Filter} filter a filter which got toggled - * @param {Boolean} default_enabled filter got enabled through the default values, at render time. - */ - do_toggle_filter: function (filter, default_enabled) { - if (default_enabled || filter.is_enabled()) { - this.enabled_filters.push(filter); - } else { - this.enabled_filters = _.without( - this.enabled_filters, filter); - } - - if (!default_enabled) { - // selecting a filter after initial loading automatically - // triggers refresh - this.$element.find('form').submit(); + keydown: function (e) { + var key = VS.app.hotkeys.key(e); + if (key !== 'right') { + return VS.ui.SearchFacet.prototype.keydown.call(this, e); } + e.preventDefault(); + this.deselectFacet(); + this.options.app.searchBox.focusNextFacet(this, 1); } }); - -/** @namespace */ -openerp.web.search = {}; /** - * Registry of search fields, called by :js:class:`openerp.web.SearchView` to + * Registry of search fields, called by :js:class:`instance.web.SearchView` to * find and instantiate its field widgets. */ -openerp.web.search.fields = new openerp.web.Registry({ - 'char': 'openerp.web.search.CharField', - 'text': 'openerp.web.search.CharField', - 'boolean': 'openerp.web.search.BooleanField', - 'integer': 'openerp.web.search.IntegerField', - 'id': 'openerp.web.search.IntegerField', - 'float': 'openerp.web.search.FloatField', - 'selection': 'openerp.web.search.SelectionField', - 'datetime': 'openerp.web.search.DateTimeField', - 'date': 'openerp.web.search.DateField', - 'many2one': 'openerp.web.search.ManyToOneField', - 'many2many': 'openerp.web.search.CharField', - 'one2many': 'openerp.web.search.CharField' +instance.web.search.fields = new instance.web.Registry({ + 'char': 'instance.web.search.CharField', + 'text': 'instance.web.search.CharField', + 'boolean': 'instance.web.search.BooleanField', + 'integer': 'instance.web.search.IntegerField', + 'id': 'instance.web.search.IntegerField', + 'float': 'instance.web.search.FloatField', + 'selection': 'instance.web.search.SelectionField', + 'datetime': 'instance.web.search.DateTimeField', + 'date': 'instance.web.search.DateField', + 'many2one': 'instance.web.search.ManyToOneField', + 'many2many': 'instance.web.search.CharField', + 'one2many': 'instance.web.search.CharField' }); -openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.search.Invalid# */{ +instance.web.search.Invalid = instance.web.Class.extend( /** @lends instance.web.search.Invalid# */{ /** * Exception thrown by search widgets when they hold invalid values, * which they can not return when asked. * - * @constructs openerp.web.search.Invalid - * @extends openerp.web.Class + * @constructs instance.web.search.Invalid + * @extends instance.web.Class * * @param field the name of the field holding an invalid value * @param value the invalid value @@ -527,92 +723,42 @@ openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.se ); } }); -openerp.web.search.Widget = openerp.web.OldWidget.extend( /** @lends openerp.web.search.Widget# */{ +instance.web.search.Widget = instance.web.OldWidget.extend( /** @lends instance.web.search.Widget# */{ template: null, /** * Root class of all search widgets * - * @constructs openerp.web.search.Widget - * @extends openerp.web.OldWidget + * @constructs instance.web.search.Widget + * @extends instance.web.OldWidget * * @param view the ancestor view of this widget */ init: function (view) { this._super(view); this.view = view; - }, - /** - * Sets and returns a globally unique identifier for the widget. - * - * If a prefix is specified, the identifier will be appended to it. - * - * @params prefix prefix sections, empty/falsy sections will be removed - */ - make_id: function () { - this.element_id = _.uniqueId( - ['search'].concat( - _.compact(_.toArray(arguments)), - ['']).join('_')); - return this.element_id; - }, - /** - * "Starts" the widgets. Called at the end of the rendering, this allows - * widgets to hook themselves to their view sections. - * - * On widgets, if they kept a reference to a view and have an element_id, - * will fetch and set their root element on $element. - */ - start: function () { - this._super(); - if (this.view && this.element_id) { - // id is unique, and no getElementById on elements - this.$element = $(document.getElementById( - this.element_id)); - } - }, - /** - * "Stops" the widgets. Called when the view destroys itself, this - * lets the widgets clean up after themselves. - */ - destroy: function () { - delete this.view; - this._super(); - }, - render: function (defaults) { - // FIXME - return this._super(_.extend(this, {defaults: defaults})); } }); -openerp.web.search.add_expand_listener = function($root) { +instance.web.search.add_expand_listener = function($root) { $root.find('a.searchview_group_string').click(function (e) { $root.toggleClass('folded expanded'); e.stopPropagation(); e.preventDefault(); }); }; -openerp.web.search.Group = openerp.web.search.Widget.extend({ +instance.web.search.Group = instance.web.search.Widget.extend({ template: 'SearchView.group', init: function (view_section, view, fields) { this._super(view); this.attrs = view_section.attrs; this.lines = view.make_widgets( view_section.children, fields); - this.make_id('group'); - }, - start: function () { - this._super(); - openerp.web.search.add_expand_listener(this.$element); - var widget_starts = _(this.lines).chain().flatten() - .map(function (widget) { return widget.start(); }) - .value(); - return $.when.apply(null, widget_starts); } }); -openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.web.search.Input# */{ +instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instance.web.search.Input# */{ /** - * @constructs openerp.web.search.Input - * @extends openerp.web.search.Widget + * @constructs instance.web.search.Input + * @extends instance.web.search.Widget * * @param view */ @@ -621,10 +767,44 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp. this.view.inputs.push(this); this.style = undefined; }, + /** + * Fetch auto-completion values for the widget. + * + * The completion values should be an array of objects with keys category, + * label, value prefixed with an object with keys type=section and label + * + * @param {String} value value to complete + * @returns {jQuery.Deferred<null|Array>} + */ + complete: function (value) { + return $.when(null) + }, + /** + * Returns a VS.model.SearchFacet instance for the provided defaults if + * they apply to this widget, or null if they don't. + * + * This default implementation will try calling + * :js:func:`instance.web.search.Input#facet_for` if the widget's name + * matches the input key + * + * @param {Object} defaults + * @returns {jQuery.Deferred<null|Object>} + */ + facet_for_defaults: function (defaults) { + if (!this.attrs || + !(this.attrs.name in defaults && defaults[this.attrs.name])) { + return $.when(null); + } + return this.facet_for(defaults[this.attrs.name]); + }, get_context: function () { throw new Error( "get_context not implemented for widget " + this.attrs.type); }, + get_groupby: function () { + throw new Error( + "get_groupby not implemented for widget " + this.attrs.type); + }, get_domain: function () { throw new Error( "get_domain not implemented for widget " + this.attrs.type); @@ -638,42 +818,80 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp. } } this.attrs = attrs; - }, - /** - * Specific clearing operations, if any - */ - clear: function () {} + } }); -openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends openerp.web.search.FilterGroup# */{ +instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends instance.web.search.FilterGroup# */{ template: 'SearchView.filters', /** * Inclusive group of filters, creates a continuous "button" with clickable * sections (the normal display for filters is to be a self-contained button) * - * @constructs openerp.web.search.FilterGroup - * @extends openerp.web.search.Input + * @constructs instance.web.search.FilterGroup + * @extends instance.web.search.Input * - * @param {Array<openerp.web.search.Filter>} filters elements of the group - * @param {openerp.web.SearchView} view view in which the filters are contained + * @param {Array<instance.web.search.Filter>} filters elements of the group + * @param {instance.web.SearchView} view view in which the filters are contained */ init: function (filters, view) { this._super(view); this.filters = filters; - this.length = filters.length; }, start: function () { - this._super(); - _.each(this.filters, function (filter) { - filter.start(); + this.$element.on('click', 'li', this.proxy('toggle_filter')); + return $.when(null); + }, + facet_for_defaults: function (defaults) { + var fs = _(this.filters).filter(function (f) { + return f.attrs && f.attrs.name && !!defaults[f.attrs.name]; }); + if (_.isEmpty(fs)) { return $.when(null); } + return $.when(new VS.model.SearchFacet({ + category: _t("Filter"), + value: _(fs).map(function (f) { + return f.attrs.string || f.attrs.name }).join(' | '), + json: fs, + field: this, + app: this.view.vs + })); + }, + /** + * Fetches contexts for all enabled filters in the group + * + * @param {VS.model.SearchFacet} facet + * @return {*} combined contexts of the enabled filters in this group + */ + get_context: function (facet) { + var contexts = _(facet.get('json')).chain() + .map(function (filter) { return filter.attrs.context; }) + .reject(_.isEmpty) + .value(); + + if (!contexts.length) { return; } + if (contexts.length === 1) { return contexts[0]; } + return _.extend(new instance.web.CompoundContext, { + __contexts: contexts + }); + }, + /** + * Fetches group_by sequence for all enabled filters in the group + * + * @param {VS.model.SearchFacet} facet + * @return {Array} enabled filters in this group + */ + get_groupby: function (facet) { + return _(facet.get('json')).chain() + .map(function (filter) { return filter.attrs.context; }) + .reject(_.isEmpty) + .value(); }, - get_context: function () { }, /** * Handles domains-fetching for all the filters within it: groups them. + * + * @param {VS.model.SearchFacet} facet + * @return {*} combined domains of the enabled filters in this group */ - get_domain: function () { - var domains = _(this.filters).chain() - .filter(function (filter) { return filter.is_enabled(); }) + get_domain: function (facet) { + var domains = _(facet.get('json')).chain() .map(function (filter) { return filter.attrs.domain; }) .reject(_.isEmpty) .value(); @@ -683,19 +901,62 @@ openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends open for (var i=domains.length; --i;) { domains.unshift(['|']); } - return _.extend(new openerp.web.CompoundDomain(), { + return _.extend(new instance.web.CompoundDomain(), { __domains: domains }); + }, + toggle_filter: function (e) { + this.toggle(this.filters[$(e.target).index()]); + }, + toggle: function (filter) { + // FIXME: oh god, my eyes, they hurt + var self = this, fs; + var facet = this.view.vs.searchQuery.detect(function (f) { + return f.get('field') === self; }); + if (facet) { + fs = facet.get('json'); + + if (_.include(fs, filter)) { + fs = _.without(fs, filter); + } else { + fs.push(filter); + } + if (_(fs).isEmpty()) { + this.view.vs.searchQuery.remove(facet, {trigger_search: true}); + } else { + facet.set({ + json: fs, + value: _(fs).map(function (f) { + return f.attrs.string || f.attrs.name }).join(' | ') + }); + } + return; + } else { + fs = [filter]; + } + + this.view.vs.searchQuery.add({ + category: _t("Filter"), + value: _(fs).map(function (f) { + return f.attrs.string || f.attrs.name }).join(' | '), + json: fs, + field: this, + app: this.view.vs + }); } }); -openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.web.search.Filter# */{ +instance.web.search.Filter = instance.web.search.Input.extend(/** @lends instance.web.search.Filter# */{ template: 'SearchView.filter', /** * Implementation of the OpenERP filters (button with a context and/or * a domain sent as-is to the search view) * - * @constructs openerp.web.search.Filter - * @extends openerp.web.search.Input + * Filters are only attributes holder, the actual work (compositing + * domains and contexts, converting between facets and filters) is + * performed by the filter group. + * + * @constructs instance.web.search.Filter + * @extends instance.web.search.Input * * @param node * @param view @@ -703,56 +964,17 @@ openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.w init: function (node, view) { this._super(view); this.load_attrs(node.attrs); - this.classes = [this.attrs.string ? 'filter_label' : 'filter_icon']; - this.make_id('filter', this.attrs.name); - }, - start: function () { - this._super(); - var self = this; - this.$element.click(function (e) { - $(this).toggleClass('enabled'); - self.view.do_toggle_filter(self); - }); - }, - /** - * Returns whether the filter is currently enabled (in use) or not. - * - * @returns a boolean - */ - is_enabled:function () { - return this.$element.hasClass('enabled'); - }, - /** - * If the filter is present in the defaults (and has a truthy value), - * enable the filter. - * - * @param {Object} defaults the search view's default values - */ - render: function (defaults) { - if (this.attrs.name && defaults[this.attrs.name]) { - this.classes.push('enabled'); - this.view.do_toggle_filter(this, true); - } - return this._super(defaults); }, - get_context: function () { - if (!this.is_enabled()) { - return; - } - return this.attrs.context; - }, - /** - * Does not return anything: filter domain is handled at the FilterGroup - * level - */ - get_domain: function () { } + facet_for: function () { return $.when(null); }, + get_context: function () { }, + get_domain: function () { }, }); -openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.web.search.Field# */ { +instance.web.search.Field = instance.web.search.Input.extend( /** @lends instance.web.search.Field# */ { template: 'SearchView.field', default_operator: '=', /** - * @constructs openerp.web.search.Field - * @extends openerp.web.search.Input + * @constructs instance.web.search.Field + * @extends instance.web.search.Input * * @param view_section * @param field @@ -761,25 +983,21 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w init: function (view_section, field, view) { this._super(view); this.load_attrs(_.extend({}, field, view_section.attrs)); - this.filters = new openerp.web.search.FilterGroup(_.compact(_.map( - view_section.children, function (filter_node) { - if (filter_node.attrs.string && - typeof console !== 'undefined' && console.debug) { - console.debug("Filter-in-field with a 'string' attribute " - + "in view", view); - } - delete filter_node.attrs.string; - return new openerp.web.search.Filter( - filter_node, view); - })), view); - this.make_id('input', field.type, this.attrs.name); }, - start: function () { - this._super(); - this.filters.start(); + facet_for: function (value) { + return $.when(new VS.model.SearchFacet({ + category: this.attrs.string || this.attrs.name, + value: String(value), + json: value, + field: this, + app: this.view.vs + })); }, - get_context: function () { - var val = this.get_value(); + get_value: function (facet) { + return facet.value(); + }, + get_context: function (facet) { + var val = this.get_value(facet); // A field needs a value to be "active", and a context to send when // active var has_value = (val !== null && val !== ''); @@ -787,10 +1005,10 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w if (!(has_value && context)) { return; } - return _.extend( - {}, context, - {own_values: {self: val}}); + return new instance.web.CompoundContext(context) + .set_eval_context({self: val}); }, + get_groupby: function () { }, /** * Function creating the returned domain for the field, override this * methods in children if you only need to customize the field's domain @@ -802,11 +1020,11 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w * @param {Number|String} value parsed value for the field * @returns {Array<Array>} domain to include in the resulting search */ - make_domain: function (name, operator, value) { - return [[name, operator, value]]; + make_domain: function (name, operator, facet) { + return [[name, operator, this.get_value(facet)]]; }, - get_domain: function () { - var val = this.get_value(); + get_domain: function (facet) { + var val = this.get_value(facet); if (val === null || val === '') { return; } @@ -816,9 +1034,10 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w return this.make_domain( this.attrs.name, this.attrs.operator || this.default_operator, - val); + facet); } - return _.extend({}, domain, {own_values: {self: val}}); + return new instance.web.CompoundDomain(domain) + .set_eval_context({self: val}); } }); /** @@ -829,15 +1048,25 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w * * The Javascript and the HTML values are identical (strings) * * @class - * @extends openerp.web.search.Field + * @extends instance.web.search.Field */ -openerp.web.search.CharField = openerp.web.search.Field.extend( /** @lends openerp.web.search.CharField# */ { +instance.web.search.CharField = instance.web.search.Field.extend( /** @lends instance.web.search.CharField# */ { default_operator: 'ilike', - get_value: function () { - return this.$element.val(); + complete: function (value) { + if (_.isEmpty(value)) { return $.when(null); } + var label = _.str.sprintf(_.str.escapeHTML( + _t("Search %(field)s for: %(value)s")), { + field: '<em>' + this.attrs.string + '</em>', + value: '<strong>' + _.str.escapeHTML(value) + '</strong>'}); + return $.when([{ + category: this.attrs.string, + label: label, + value: value, + field: this + }]); } }); -openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends openerp.web.search.NumberField# */{ +instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{ get_value: function () { if (!this.$element.val()) { return null; @@ -846,7 +1075,7 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open check = Number(this.$element.val()); if (isNaN(val) || val !== check) { this.$element.addClass('error'); - throw new openerp.web.search.Invalid( + throw new instance.web.search.Invalid( this.attrs.name, this.$element.val(), this.error_message); } this.$element.removeClass('error'); @@ -855,13 +1084,13 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open }); /** * @class - * @extends openerp.web.search.NumberField + * @extends instance.web.search.NumberField */ -openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.IntegerField# */{ +instance.web.search.IntegerField = instance.web.search.NumberField.extend(/** @lends instance.web.search.IntegerField# */{ error_message: _t("not a valid integer"), parse: function (value) { try { - return openerp.web.parse_value(value, {'widget': 'integer'}); + return instance.web.parse_value(value, {'widget': 'integer'}); } catch (e) { return NaN; } @@ -869,13 +1098,13 @@ openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @len }); /** * @class - * @extends openerp.web.search.NumberField + * @extends instance.web.search.NumberField */ -openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.FloatField# */{ +instance.web.search.FloatField = instance.web.search.NumberField.extend(/** @lends instance.web.search.FloatField# */{ error_message: _t("not a valid number"), parse: function (value) { try { - return openerp.web.parse_value(value, {'widget': 'float'}); + return instance.web.parse_value(value, {'widget': 'float'}); } catch (e) { return NaN; } @@ -883,9 +1112,9 @@ openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends }); /** * @class - * @extends openerp.web.search.Field + * @extends instance.web.search.Field */ -openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends openerp.web.search.SelectionField# */{ +instance.web.search.SelectionField = instance.web.search.Field.extend(/** @lends instance.web.search.SelectionField# */{ // This implementation is a basic <select> field, but it may have to be // altered to be more in line with the GTK client, which uses a combo box // (~ jquery.autocomplete): @@ -904,48 +1133,48 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o return !item[1]; }); }, - get_value: function () { - var index = parseInt(this.$element.val(), 10); - if (isNaN(index)) { return null; } - var value = this.attrs.selection[index][0]; - if (value === false) { return null; } - return value; - }, - /** - * The selection field needs a default ``false`` value in case none is - * provided, so that selector options with a ``false`` value (convention - * for explicitly empty options) get selected by default rather than the - * first (value-holding) option in the selection. - * - * @param {Object} defaults search default values - */ - render: function (defaults) { - if (!defaults[this.attrs.name]) { - defaults[this.attrs.name] = false; - } - return this._super(defaults); - }, - clear: function () { - var self = this, d = $.Deferred(), selection = this.attrs.selection; - for(var index=0; index<selection.length; ++index) { - var item = selection[index]; - if (!item[1]) { - setTimeout(function () { - // won't override mutable, because we immediately bail out - //noinspection JSReferencingMutableVariableFromClosure - self.$element.val(index); - d.resolve(); - }, 0); - return d.promise(); - } - } - return d.resolve().promise(); + complete: function (needle) { + var self = this; + var results = _(this.attrs.selection).chain() + .filter(function (sel) { + var value = sel[0], label = sel[1]; + if (!value) { return false; } + return label.toLowerCase().indexOf(needle.toLowerCase()) !== -1; + }) + .map(function (sel) { + return { + category: self.attrs.string, + field: self, + value: sel[1], + json: sel[0] + }; + }).value(); + if (_.isEmpty(results)) { return $.when(null); } + return $.when.apply(null, [{ + category: this.attrs.string + }].concat(results)); + }, + facet_for: function (value) { + var match = _(this.attrs.selection).detect(function (sel) { + return sel[0] === value; + }); + if (!match) { return $.when(null); } + return $.when(new VS.model.SearchFacet({ + category: this.attrs.string, + value: match[1], + json: match[0], + field: this, + app: this.view.app + })); + }, + get_value: function (facet) { + return facet.get('json'); } }); -openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @lends openerp.web.search.BooleanField# */{ +instance.web.search.BooleanField = instance.web.search.SelectionField.extend(/** @lends instance.web.search.BooleanField# */{ /** - * @constructs openerp.web.search.BooleanField - * @extends openerp.web.search.BooleanField + * @constructs instance.web.search.BooleanField + * @extends instance.web.search.BooleanField */ init: function () { this._super.apply(this, arguments); @@ -954,25 +1183,8 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @ ['false', _t("No")] ]; }, - /** - * Search defaults likely to be boolean values (for a boolean field). - * - * In the HTML, we only want/get strings, and our strings here are ``true`` - * and ``false``, so ensure we use precisely those by truth-testing the - * default value (iif there is one in the view's defaults). - * - * @param {Object} defaults default values for this search view - * @returns {String} rendered boolean field - */ - render: function (defaults) { - var name = this.attrs.name; - if (name in defaults) { - defaults[name] = defaults[name] ? "true" : "false"; - } - return this._super(defaults); - }, - get_value: function () { - switch (this._super()) { + get_value: function (facet) { + switch (this._super(facet)) { case 'false': return false; case 'true': return true; default: return null; @@ -981,26 +1193,27 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @ }); /** * @class - * @extends openerp.web.search.DateField + * @extends instance.web.search.DateField */ -openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{ - template: "SearchView.date", - start: function () { - this._super(); - // FIXME: this insanity puts a div inside a span - this.datewidget = new openerp.web.DateWidget(this); - this.datewidget.prependTo(this.$element); - this.datewidget.$element.find("input") - .attr("size", 15) - .attr("autofocus", this.attrs.default_focus === '1' ? 'autofocus' : null) - .removeAttr('style'); - this.datewidget.set_value(this.defaults[this.attrs.name] || false); - }, - get_value: function () { - return this.datewidget.get_value() || null; - }, - clear: function () { - this.datewidget.set_value(false); +instance.web.search.DateField = instance.web.search.Field.extend(/** @lends instance.web.search.DateField# */{ + get_value: function (facet) { + return instance.web.date_to_str(facet.get('json')); + }, + complete: function (needle) { + var d = Date.parse(needle); + if (!d) { return $.when(null); } + var value = instance.web.format_value(d, this.attrs); + var label = _.str.sprintf(_.str.escapeHTML( + _t("Search %(field)s at: %(value)s")), { + field: '<em>' + this.attrs.string + '</em>', + value: '<strong>' + value + '</strong>'}); + return $.when([{ + category: this.attrs.string, + label: label, + value: value, + json: d, + field: this + }]); } }); /** @@ -1012,205 +1225,135 @@ openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends opener * spanning the whole day selected by the date widget * * @class - * @extends openerp.web.DateField + * @extends instance.web.DateField */ -openerp.web.search.DateTimeField = openerp.web.search.DateField.extend(/** @lends openerp.web.search.DateTimeField# */{ - make_domain: function (name, operator, value) { - return ['&', [name, '>=', value + ' 00:00:00'], - [name, '<=', value + ' 23:59:59']]; +instance.web.search.DateTimeField = instance.web.search.DateField.extend(/** @lends instance.web.search.DateTimeField# */{ + get_value: function (facet) { + return instance.web.datetime_to_str(facet.get('json')); } }); -openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({ +instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ init: function (view_section, field, view) { this._super(view_section, field, view); + this.model = new instance.web.Model(this.attrs.relation); + }, + complete: function (needle) { var self = this; - this.got_name = $.Deferred().then(function () { - self.$element.val(self.name); + // TODO: context + // FIXME: "concurrent" searches (multiple requests, mis-ordered responses) + return this.model.call('name_search', [], { + name: needle, + limit: 8, + context: {} + }).pipe(function (results) { + if (_.isEmpty(results)) { return null; } + return [{category: self.attrs.string}].concat( + _(results).map(function (result) { + return { + category: self.attrs.string, + value: result[1], + json: result[0], + field: self + }; + })); }); - this.model = new openerp.web.Model(this.attrs['relation']); - - this.orderer = new openerp.web.DropMisordered(); - }, - start: function () { - this._super(); - this.setup_autocomplete(); - var started = $.Deferred(); - this.got_name.then(function () { started.resolve();}, - function () { started.resolve(); }); - return started.promise(); }, - setup_autocomplete: function () { + facet_for: function (value) { var self = this; - this.$element.autocomplete({ - source: function (req, resp) { - self.orderer.add(self.model.call('name_search', null, { - name: req.term, - args: self.attrs.domain, - limit: 8 - })).then(function (data) { - resp(_.map(data, function (result) { - return {id: result[0], label: result[1]} - })); - }); - }, - select: function (event, ui) { - self.id = ui.item.id; - self.name = ui.item.label; - }, - delay: 100 - }) - }, - on_name_get: function (name_get) { - if (!name_get.length) { - delete this.id; - this.got_name.reject(); - return; - } - this.name = name_get[0][1]; - this.got_name.resolve(); - }, - render: function (defaults) { - if (defaults[this.attrs.name]) { - this.id = defaults[this.attrs.name]; - if (this.id instanceof Array) - this.id = this.id[0]; - // TODO: maybe this should not be completely removed - delete defaults[this.attrs.name]; - this.model.call('name_get', [[this.id]]).then( - this.proxy('on_name_get')); - } else { - this.got_name.reject(); + if (value instanceof Array) { + return $.when(new VS.model.SearchFacet({ + category: this.attrs.string, + value: value[1], + json: value[0], + field: this, + app: this.view.vs + })); } - return this._super(defaults); + return this.model.call('name_get', [value], {}).pipe(function (names) { + return new VS.model.SearchFacet({ + category: self.attrs.string, + value: names[0][1], + json: names[0][0], + field: self, + app: self.view.vs + }); + }) }, - make_domain: function (name, operator, value) { - if (this.id && this.name) { - if (value === this.name) { - return [[name, '=', this.id]]; - } else { - delete this.id; - delete this.name; - } + make_domain: function (name, operator, facet) { + // ``json`` -> actual auto-completed id + if (facet.get('json')) { + return [[name, '=', facet.get('json')]]; } - return this._super(name, operator, value); + + return this._super(name, operator, facet); } }); -openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({ - template: 'SearchView.extended_search', - init: function (parent, model) { - this._super(parent); - this.model = model; - }, - add_group: function() { - var group = new openerp.web.search.ExtendedSearchGroup(this, this.fields); - group.appendTo(this.$element.find('.searchview_extended_groups_list')); - this.check_last_element(); - }, +instance.web.search.Advanced = instance.web.search.Input.extend({ + template: 'SearchView.advanced', start: function () { - this._super(); - this.$element.closest("table.oe-searchview-render-line").css("display", "none"); var self = this; - this.rpc("/web/searchview/fields_get", - {"model": this.model}, function(data) { - self.fields = data.fields; - if (!('id' in self.fields)) { - self.fields.id = { - string: 'ID', - type: 'id' - } - } - openerp.web.search.add_expand_listener(self.$element); - self.$element.find('.searchview_extended_add_group').click(function (e) { - self.add_group(); + this.$element + .on('keypress keydown keyup', function (e) { e.stopPropagation(); }) + .on('click', 'h4', function () { + self.$element.toggleClass('oe_opened'); + }).on('click', 'button.oe_add_condition', function () { + self.append_proposition(); + }).on('submit', 'form', function (e) { + e.preventDefault(); + self.commit_search(); }); + return $.when( + this._super(), + this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) { + self.fields = _.extend({ + id: { string: 'ID', type: 'id' } + }, data.fields); + })).then(function () { + self.append_proposition(); }); }, - get_context: function() { - return null; - }, - get_domain: function() { - if (!this.$element) { - return null; // not a logical state but sometimes it happens - } - if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") { - return null; - } - return _.reduce(this.getChildren(), - function(mem, x) { return mem.concat(x.get_domain());}, []); - }, - on_activate: function() { - this.add_group(); - var table = this.$element.closest("table.oe-searchview-render-line"); - table.css("display", ""); - if(this.$element.hasClass("folded")) { - this.$element.toggleClass("folded expanded"); - } - }, - hide: function() { - var table = this.$element.closest("table.oe-searchview-render-line"); - table.css("display", "none"); - if(this.$element.hasClass("expanded")) { - this.$element.toggleClass("folded expanded"); - } - }, - check_last_element: function() { - _.each(this.getChildren(), function(x) {x.set_last_group(false);}); - if (this.getChildren().length >= 1) { - this.getChildren()[this.getChildren().length - 1].set_last_group(true); - } - } -}); - -openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({ - template: 'SearchView.extended_search.group', - init: function (parent, fields) { - this._super(parent); - this.fields = fields; - }, - add_prop: function() { - var prop = new openerp.web.search.ExtendedSearchProposition(this, this.fields); - var render = prop.render({'index': this.getChildren().length - 1}); - this.$element.find('.searchview_extended_propositions_list').append(render); - prop.start(); + append_proposition: function () { + return (new instance.web.search.ExtendedSearchProposition(this, this.fields)) + .appendTo(this.$element.find('ul')); }, - start: function () { - var _this = this; - this.add_prop(); - this.$element.find('.searchview_extended_add_proposition').click(function () { - _this.add_prop(); + commit_search: function () { + var self = this; + // Get domain sections from all propositions + var children = this.getChildren(), + domain = _.invoke(children, 'get_proposition'); + var filters = _(domain).map(function (section) { + return new instance.web.search.Filter({attrs: { + string: _.str.sprintf('%s(%s)%s', + section[0], section[1], section[2]), + domain: [section] + }}, self.view); }); - this.$element.find('.searchview_extended_delete_group').click(function () { - _this.destroy(); + // Create Filter (& FilterGroup around it) with that domain + var f = new instance.web.search.FilterGroup(filters, this.view); + // add group to query + this.view.vs.searchQuery.add({ + category: _t("Advanced"), + value: _(filters).map(function (f) { + return f.attrs.string || f.attrs.name }).join(' | '), + json: filters, + field: f, + app: this.view.vs }); - }, - get_domain: function() { - var props = _(this.getChildren()).chain().map(function(x) { - return x.get_proposition(); - }).compact().value(); - var choice = this.$element.find(".searchview_extended_group_choice").val(); - var op = choice == "all" ? "&" : "|"; - return choice == "none" ? ['!'] : [].concat( - _.map(_.range(_.max([0,props.length - 1])), function() { return op; }), - props); - }, - destroy: function() { - var parent = this.getParent(); - if (this.getParent().getChildren().length == 1) - this.getParent().hide(); - this._super(); - parent.check_last_element(); - }, - set_last_group: function(is_last) { - this.$element.toggleClass('last_group', is_last); + // remove all propositions + _.invoke(children, 'destroy'); + // add new empty proposition + this.append_proposition(); + // TODO: API on searchview + this.view.$element.removeClass('oe_searchview_open_drawer'); } }); -openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** @lends openerp.web.search.ExtendedSearchProposition# */{ +instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{ template: 'SearchView.extended_search.proposition', /** - * @constructs openerp.web.search.ExtendedSearchProposition - * @extends openerp.web.OldWidget + * @constructs instance.web.search.ExtendedSearchProposition + * @extends instance.web.OldWidget * * @param parent * @param fields @@ -1225,8 +1368,6 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** this.value = null; }, start: function () { - this.$element = $("#" + this.element_id); - this.select_field(this.fields.length > 0 ? this.fields[0] : null); var _this = this; this.$element.find(".searchview_extended_prop_field").change(function() { _this.changed(); @@ -1234,14 +1375,7 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** this.$element.find('.searchview_extended_delete_prop').click(function () { _this.destroy(); }); - }, - destroy: function() { - var parent; - if (this.getParent().getChildren().length == 1) - parent = this.getParent(); - this._super(); - if (parent) - parent.destroy(); + this.changed(); }, changed: function() { var nval = this.$element.find(".searchview_extended_prop_field").val(); @@ -1267,17 +1401,11 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** } var type = field.type; - try { - openerp.web.search.custom_filters.get_object(type); - } catch (e) { - if (! e instanceof openerp.web.KeyNotFound) { - throw e; - } - type = "char"; - console.log('Unknow field type ' + e.key); + var obj = instance.web.search.custom_filters.get_object(type); + if(obj === null) { + obj = instance.web.search.custom_filters.get_object("char"); } - this.value = new (openerp.web.search.custom_filters.get_object(type)) - (this); + this.value = new (obj) (this); if(this.value.set_field) { this.value.set_field(field); } @@ -1301,28 +1429,24 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** } }); -openerp.web.search.ExtendedSearchProposition.Field = openerp.web.OldWidget.extend({ +instance.web.search.ExtendedSearchProposition.Field = instance.web.OldWidget.extend({ start: function () { this.$element = $("#" + this.element_id); } }); -openerp.web.search.ExtendedSearchProposition.Char = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Char = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.char', operators: [ {value: "ilike", text: _lt("contains")}, {value: "not ilike", text: _lt("doesn't contain")}, {value: "=", text: _lt("is equal to")}, - {value: "!=", text: _lt("is not equal to")}, - {value: ">", text: _lt("greater than")}, - {value: "<", text: _lt("less than")}, - {value: ">=", text: _lt("greater or equal than")}, - {value: "<=", text: _lt("less or equal than")} + {value: "!=", text: _lt("is not equal to")} ], get_value: function() { return this.$element.val(); } }); -openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.empty', operators: [ {value: "=", text: _lt("is equal to")}, @@ -1337,11 +1461,11 @@ openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.search.Exten }, start: function() { this._super(); - this.datewidget = new openerp.web.DateTimeWidget(this); + this.datewidget = new instance.web.DateTimeWidget(this); this.datewidget.prependTo(this.$element); } }); -openerp.web.search.ExtendedSearchProposition.Date = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Date = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.empty', operators: [ {value: "=", text: _lt("is equal to")}, @@ -1356,11 +1480,11 @@ openerp.web.search.ExtendedSearchProposition.Date = openerp.web.search.ExtendedS }, start: function() { this._super(); - this.datewidget = new openerp.web.DateWidget(this); + this.datewidget = new instance.web.DateWidget(this); this.datewidget.prependTo(this.$element); } }); -openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.integer', operators: [ {value: "=", text: _lt("is equal to")}, @@ -1372,16 +1496,16 @@ openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.search.Extend ], get_value: function() { try { - return openerp.web.parse_value(this.$element.val(), {'widget': 'integer'}); + return instance.web.parse_value(this.$element.val(), {'widget': 'integer'}); } catch (e) { return ""; } } }); -openerp.web.search.ExtendedSearchProposition.Id = openerp.web.search.ExtendedSearchProposition.Integer.extend({ +instance.web.search.ExtendedSearchProposition.Id = instance.web.search.ExtendedSearchProposition.Integer.extend({ operators: [{value: "=", text: _lt("is")}] }); -openerp.web.search.ExtendedSearchProposition.Float = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Float = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.float', operators: [ {value: "=", text: _lt("is equal to")}, @@ -1393,13 +1517,13 @@ openerp.web.search.ExtendedSearchProposition.Float = openerp.web.search.Extended ], get_value: function() { try { - return openerp.web.parse_value(this.$element.val(), {'widget': 'float'}); + return instance.web.parse_value(this.$element.val(), {'widget': 'float'}); } catch (e) { return ""; } } }); -openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Selection = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.selection', operators: [ {value: "=", text: _lt("is")}, @@ -1412,7 +1536,7 @@ openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.search.Exte return this.$element.val(); } }); -openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.search.ExtendedSearchProposition.Field.extend({ +instance.web.search.ExtendedSearchProposition.Boolean = instance.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.boolean', operators: [ {value: "=", text: _lt("is true")}, @@ -1423,21 +1547,21 @@ openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.search.Extend } }); -openerp.web.search.custom_filters = new openerp.web.Registry({ - 'char': 'openerp.web.search.ExtendedSearchProposition.Char', - 'text': 'openerp.web.search.ExtendedSearchProposition.Char', - 'one2many': 'openerp.web.search.ExtendedSearchProposition.Char', - 'many2one': 'openerp.web.search.ExtendedSearchProposition.Char', - 'many2many': 'openerp.web.search.ExtendedSearchProposition.Char', +instance.web.search.custom_filters = new instance.web.Registry({ + 'char': 'instance.web.search.ExtendedSearchProposition.Char', + 'text': 'instance.web.search.ExtendedSearchProposition.Char', + 'one2many': 'instance.web.search.ExtendedSearchProposition.Char', + 'many2one': 'instance.web.search.ExtendedSearchProposition.Char', + 'many2many': 'instance.web.search.ExtendedSearchProposition.Char', - 'datetime': 'openerp.web.search.ExtendedSearchProposition.DateTime', - 'date': 'openerp.web.search.ExtendedSearchProposition.Date', - 'integer': 'openerp.web.search.ExtendedSearchProposition.Integer', - 'float': 'openerp.web.search.ExtendedSearchProposition.Float', - 'boolean': 'openerp.web.search.ExtendedSearchProposition.Boolean', - 'selection': 'openerp.web.search.ExtendedSearchProposition.Selection', + 'datetime': 'instance.web.search.ExtendedSearchProposition.DateTime', + 'date': 'instance.web.search.ExtendedSearchProposition.Date', + 'integer': 'instance.web.search.ExtendedSearchProposition.Integer', + 'float': 'instance.web.search.ExtendedSearchProposition.Float', + 'boolean': 'instance.web.search.ExtendedSearchProposition.Boolean', + 'selection': 'instance.web.search.ExtendedSearchProposition.Selection', - 'id': 'openerp.web.search.ExtendedSearchProposition.Id' + 'id': 'instance.web.search.ExtendedSearchProposition.Id' }); }; diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js index 2c0dc3d70b4..59d1fed9345 100644 --- a/addons/web/static/src/js/view_editor.js +++ b/addons/web/static/src/js/view_editor.js @@ -1,15 +1,15 @@ -openerp.web.view_editor = function(openerp) { -var _t = openerp.web._t; -var QWeb = openerp.web.qweb; -openerp.web.ViewEditor = openerp.web.OldWidget.extend({ +openerp.web.view_editor = function(instance) { +var _t = instance.web._t; +var QWeb = instance.web.qweb; +instance.web.ViewEditor = instance.web.OldWidget.extend({ init: function(parent, element_id, dataset, view, options) { this._super(parent); this.element_id = element_id; this.parent = parent; - this.dataset = new openerp.web.DataSetSearch(this, 'ir.ui.view', null, null), + this.dataset = new instance.web.DataSetSearch(this, 'ir.ui.view', null, null), this.model = dataset.model; this.xml_element_id = 0; - this.property = openerp.web.ViewEditor.property_widget; + this.property = instance.web.ViewEditor.property_widget; this.one_object = false; }, start: function() { @@ -39,7 +39,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ select_view_id: self.parent.fields_view.view_id } }; - this.view_edit_dialog = new openerp.web.Dialog(this, { + this.view_edit_dialog = new instance.web.Dialog(this, { title: action_title, width: 850, buttons: [ @@ -51,7 +51,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }).open(); this.view_edit_dialog.on_close.add_last(function(){window.location.reload();}); this.main_view_id = this.parent.fields_view.view_id; - this.action_manager = new openerp.web.ActionManager(this); + this.action_manager = new instance.web.ActionManager(this); this.action_manager.appendTo(this.view_edit_dialog); $.when(this.action_manager.do_action(action)).then(function() { var viewmanager = self.action_manager.inner_viewmanager, @@ -67,7 +67,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }, on_create_view: function() { var self = this; - this.create_view_dialog = new openerp.web.Dialog(this, { + this.create_view_dialog = new instance.web.Dialog(this, { title: _.str.sprintf(_t("Create a view (%s)"), self.model), buttons: [ {text: _t("Save"), click: function () { @@ -115,8 +115,8 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }, do_save_view: function(values) { def = $.Deferred(); - var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null); - var model_dataset = new openerp.web.DataSetSearch(this, 'ir.model', null, null); + var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null); + var model_dataset = new instance.web.DataSetSearch(this, 'ir.model', null, null); var view_string = "", field_name = false, self = this; field_dataset.call( 'fields_get', [], function(fields) { _.each(['name', 'x_name'], function(value) { @@ -360,7 +360,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ edit_view: function(one_object) { var self = this; this.one_object = one_object; - this.edit_xml_dialog = new openerp.web.Dialog(this, { + this.edit_xml_dialog = new instance.web.Dialog(this, { title: _.str.sprintf(_t("View Editor %d - %s"), self.main_view_id, self.model), height: '90%', buttons: [ @@ -392,7 +392,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ action_buttons: false } }; - var action_manager = new openerp.web.ActionManager(self); + var action_manager = new instance.web.ActionManager(self); action_manager.do_action(action); }}, {text: _t("Close"), click: function(){ @@ -517,7 +517,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ var self = this; var tr = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], [])[0].att_list[0]; var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0]; - var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null); + var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null); parent_tr = $(parent_tr).find('a').text(); field_dataset.call( 'fields_get', [], function(result) { var fields = _.keys(result); @@ -811,7 +811,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }, on_edit_node: function(properties){ var self = this; - this.edit_node_dialog = new openerp.web.Dialog(this,{ + this.edit_node_dialog = new instance.web.Dialog(this,{ title: _t("Properties"), width: 450, buttons: [ @@ -880,7 +880,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ this.edit_widget = []; self.ready = $.when(self.on_groups(properties)).then(function () { _PROPERTIES_ATTRIBUTES['groups']['selection'] = self.groups; - var values = _.keys( openerp.web.form.widgets.map); + var values = _.keys( instance.web.form.widgets.map); values.push(''); values.sort(); _PROPERTIES_ATTRIBUTES['widget']['selection'] = values; @@ -908,8 +908,8 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ def.resolve(); } var group_ids = [], group_names = {}, groups = []; - var res_groups = new openerp.web.DataSetSearch(this,'res.groups', null, null), - model_data = new openerp.web.DataSetSearch(self,'ir.model.data', null, null); + var res_groups = new instance.web.DataSetSearch(this,'res.groups', null, null), + model_data = new instance.web.DataSetSearch(self,'ir.model.data', null, null); res_groups.read_slice([], {}).done(function (res_grp) { _.each(res_grp, function (res) { var key = res.id; @@ -935,7 +935,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ {'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'}, {'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}]; this.add_widget = []; - this.add_node_dialog = new openerp.web.Dialog(this,{ + this.add_node_dialog = new instance.web.Dialog(this,{ title: _t("Properties"), width: 450, buttons: [ @@ -980,7 +980,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }); table_selector.find("td[id^=]").attr("width","100px"); self.add_node_dialog.$element.find('#new_field').click(function() { - model_data = new openerp.web.DataSetSearch(self,'ir.model', null, null); + model_data = new instance.web.DataSetSearch(self,'ir.model', null, null); model_data.read_slice([], {domain: [['model','=', self.model]]}).then(function(result) { self.render_new_field(result[0].id); }); @@ -998,7 +998,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ action_buttons: true } }; - var action_manager = new openerp.web.ActionManager(self); + var action_manager = new instance.web.ActionManager(self); $.when(action_manager.do_action(action)).then(function() { var controller = action_manager.dialog_viewmanager.views['form'].controller; controller.on_button_cancel.add_last(function(){ @@ -1015,7 +1015,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({ }); } }); -openerp.web.ViewEditor.Field = openerp.web.Class.extend({ +instance.web.ViewEditor.Field = instance.web.Class.extend({ init: function(view, widget) { this.$element = view.$element; this.dirty = false; @@ -1041,7 +1041,7 @@ openerp.web.ViewEditor.Field = openerp.web.Class.extend({ validate: function() { this.is_invalid = false; try { - var value = openerp.web.parse_value(this.get_value(), this, ''); + var value = instance.web.parse_value(this.get_value(), this, ''); this.is_invalid = this.required && value === ''; } catch(e) { this.is_invalid = true; @@ -1051,7 +1051,7 @@ openerp.web.ViewEditor.Field = openerp.web.Class.extend({ return _.str.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this})) } }); -openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({ +instance.web.ViewEditor.FieldBoolean = instance.web.ViewEditor.Field.extend({ template : "vieweditor_boolean", start: function() { var self = this; @@ -1069,7 +1069,7 @@ openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({ return this.$element.find("input[id=" + this.name + "]").is(':checked')? "1" : null; } }); -openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({ +instance.web.ViewEditor.FieldChar = instance.web.ViewEditor.Field.extend({ template : "vieweditor_char", start: function () { var self = this; @@ -1085,7 +1085,7 @@ openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({ return this.$element.find("input[id=" + this.name + "]").val(); } }); -openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({ +instance.web.ViewEditor.FieldSelect = instance.web.ViewEditor.Field.extend({ template : "vieweditor_selection", start: function () { var self = this; @@ -1115,7 +1115,7 @@ openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({ return this.$element.find("select[id=" + this.name + "]").val(); } }); -openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.extend({ +instance.web.ViewEditor.FieldSelectMulti = instance.web.ViewEditor.FieldSelect.extend({ start: function () { this._super(); this.$element.find("select[id=" + this.name + "]").css('height', '100px').attr("multiple", true); @@ -1131,7 +1131,7 @@ openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.ext }); } }); -openerp.web.ViewEditor.FieldFloat = openerp.web.ViewEditor.FieldChar.extend({ +instance.web.ViewEditor.FieldFloat = instance.web.ViewEditor.FieldChar.extend({ }); var _PROPERTIES = { @@ -1196,11 +1196,11 @@ var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD', 'terp-sale', 'terp-tools', 'terp-administration', 'terp-hr', 'terp-partner', 'terp-project', 'terp-report', 'terp-stock', 'terp-calendar', 'terp-graph' ]; -openerp.web.ViewEditor.property_widget = new openerp.web.Registry({ - 'boolean' : 'openerp.web.ViewEditor.FieldBoolean', - 'selection_multi' : 'openerp.web.ViewEditor.FieldSelectMulti', - 'selection' : 'openerp.web.ViewEditor.FieldSelect', - 'char' : 'openerp.web.ViewEditor.FieldChar', - 'float' : 'openerp.web.ViewEditor.FieldFloat' +instance.web.ViewEditor.property_widget = new instance.web.Registry({ + 'boolean' : 'instance.web.ViewEditor.FieldBoolean', + 'selection_multi' : 'instance.web.ViewEditor.FieldSelectMulti', + 'selection' : 'instance.web.ViewEditor.FieldSelect', + 'char' : 'instance.web.ViewEditor.FieldChar', + 'float' : 'instance.web.ViewEditor.FieldFloat' }); }; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 50c5dcb976a..7e5b173fd37 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1,31 +1,32 @@ -openerp.web.form = function (openerp) { +openerp.web.form = function (instance) { +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; -var _t = openerp.web._t, - _lt = openerp.web._lt; -var QWeb = openerp.web.qweb; +/** @namespace */ +instance.web.form = {}; -openerp.web.views.add('form', 'openerp.web.FormView'); -openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */{ +instance.web.views.add('form', 'instance.web.FormView'); +instance.web.FormView = instance.web.View.extend({ /** * Indicates that this view is not searchable, and thus that no search * view should be displayed (if there is one active). */ searchable: false, - readonly : false, - form_template: "FormView", + template: "FormView", display_name: _lt('Form'), + view_type: "form", /** - * @constructs openerp.web.FormView - * @extends openerp.web.View + * @constructs instance.web.FormView + * @extends instance.web.View * - * @param {openerp.web.Session} session the current openerp session - * @param {openerp.web.DataSet} dataset the dataset this view will work with + * @param {instance.web.Session} session the current openerp session + * @param {instance.web.DataSet} dataset the dataset this view will work with * @param {String} view_id the identifier of the OpenERP view object * @param {Object} options - * - sidebar : [true|false] * - resize_textareas : [true|false|max_height] * - * @property {openerp.web.Registry} registry=openerp.web.form.widgets widgets registry for this form view instance + * @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance */ init: function(parent, dataset, view_id, options) { this._super(parent); @@ -34,98 +35,86 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.model = dataset.model; this.view_id = view_id || false; this.fields_view = {}; - this.widgets = {}; - this.widgets_counter = 0; this.fields = {}; this.fields_order = []; this.datarecord = {}; this.default_focus_field = null; this.default_focus_button = null; - this.registry = openerp.web.form.widgets; + this.fields_registry = instance.web.form.widgets; + this.tags_registry = instance.web.form.tags; this.has_been_loaded = $.Deferred(); - this.$form_header = null; this.translatable_fields = []; _.defaults(this.options, { - "not_interactible_on_create": false + "not_interactible_on_create": false, + "initial_mode": "view", }); this.is_initialized = $.Deferred(); this.mutating_mutex = new $.Mutex(); this.on_change_mutex = new $.Mutex(); this.reload_mutex = new $.Mutex(); - }, - start: function() { - this._super(); - return this.init_view(); - }, - init_view: function() { - if (this.embedded_view) { - var def = $.Deferred().then(this.on_loaded); - var self = this; - $.async_when().then(function() {def.resolve(self.embedded_view);}); - return def.promise(); - } else { - var context = new openerp.web.CompoundContext(this.dataset.get_context()); - return this.rpc("/web/view/load", { - "model": this.model, - "view_id": this.view_id, - "view_type": "form", - toolbar: this.options.sidebar, - context: context - }, this.on_loaded); - } + this.mode = null; + this.rendering_engine = new instance.web.form.FormRenderingEngineReadonly(this); + this.qweb = null; // A QWeb instance will be created if the view is a QWeb template }, destroy: function() { - if (this.sidebar) { - this.sidebar.attachments.destroy(); - this.sidebar.destroy(); - } - _.each(this.widgets, function(w) { + _.each(this.get_widgets(), function(w) { w.destroy(); }); this._super(); }, - reposition: function ($e) { - this.$element = $e; - this.on_loaded(); - }, on_loaded: function(data) { var self = this; if (!data) { - throw "No data provided."; + throw new Error("No data provided."); } - if (this.root_frame) { - throw "Form view does not support multiple calls to on_loaded"; + if (this.arch) { + throw "Form view does not support multiple calls to on_loaded"; } this.fields_order = []; this.fields_view = data; - this.rendered = QWeb.render(this.form_template, {'widget': this}); - this.$element.html(this.rendered); - - this.root_frame = instanciate_widget(this.registry.get_object('frame'), this, this.fields_view.arch); - var to_append = $(".oe_form_header", this.$element); - this.root_frame.appendTo(to_append.length > 0 ? to_append : this.$element); - this.root_frame.$element.children().unwrap(); - - this.$form_header = this.$element.find('.oe_form_header:first'); - this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() { + this.rendering_engine.set_fields_registry(this.fields_registry); + this.rendering_engine.set_tags_registry(this.tags_registry); + if (!this.extract_qweb_template(data)) { + this.rendering_engine.set_fields_view(data); + var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container'); + this.rendering_engine.render_to($dest); + } + + this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar'); + + this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self})); + if (this.options.$buttons) { + this.$buttons.appendTo(this.options.$buttons); + } else { + this.$element.find('.oe_form_buttons').replaceWith(this.$buttons); + } + this.$buttons + .on('click','button.oe_form_button_save',this.on_button_save) + .on('click','button.oe_form_button_cancel',this.on_button_cancel) + .on('click','button.oe_form_button_edit',this.on_button_edit) + .on('click','button.oe_form_button_create',this.on_button_create) + .on('click','button.oe_form_button_duplicate',this.on_button_duplicate) + .on('click','button.oe_form_button_delete',this.on_button_delete); + + this.$pager = $(QWeb.render("FormView.pager", {'widget':self})); + if (this.options.$pager) { + this.$pager.appendTo(this.options.$pager); + } else { + this.$element.find('.oe_form_pager').replaceWith(this.$pager); + } + this.$pager.on('click','a[data-pager-action]',function() { var action = $(this).data('pager-action'); self.on_pager_action(action); }); - this.$form_header.find('button.oe_form_button_save').click(this.on_button_save); - this.$form_header.find('button.oe_form_button_cancel').click(this.on_button_cancel); - - if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) { - this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); - this.sidebar.start(); - this.sidebar.do_unfold(); - this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this); - this.sidebar.add_toolbar(this.fields_view.toolbar); - this.set_common_sidebar_sections(this.sidebar); - - this.sidebar.add_section(_t('Customize'), 'customize'); - this.sidebar.add_items('customize', [{ + if (!this.sidebar && this.options.$sidebar) { + this.sidebar = new instance.web.Sidebar(this); + this.sidebar.appendTo(this.$sidebar); + if(this.fields_view.toolbar) { + this.sidebar.add_toolbar(this.fields_view.toolbar); + } + this.sidebar.add_items('other', [{ label: _t('Set Default'), form: this, callback: function (item) { @@ -133,9 +122,61 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }]); } + this.on("change:mode", this, this.switch_mode); + this.set({mode: this.options.initial_mode}); this.has_been_loaded.resolve(); + return $.when(); + }, + extract_qweb_template: function(fvg) { + for (var i=0, ii=fvg.arch.children.length; i < ii; i++) { + var child = fvg.arch.children[i]; + if (child.tag === "templates") { + this.qweb = new QWeb2.Engine(); + this.qweb.add_template(instance.web.json_node_to_xml(child)); + if (!this.qweb.has_template('form')) { + throw new Error("No QWeb template found for form view"); + } + return true; + } + } + this.qweb = null; + return false; + }, + get_fvg_from_qweb: function(record) { + var view = this.qweb.render('form', this.get_qweb_context(record)); + var fvg = _.clone(this.fields_view); + fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild); + return fvg; + }, + get_qweb_context: function(record) { + var self = this, + new_record = {}; + _.each(record, function(value_, name) { + var r = _.clone(self.fields_view.fields[name] || {}); + if ((r.type === 'date' || r.type === 'datetime') && value_) { + r.raw_value = instance.web.auto_str_to_date(value_); + } else { + r.raw_value = value_; + } + r.value = instance.web.format_value(value_, r); + new_record[name] = r; + }); + return { + record : new_record, + new_record : !record.id + }; + }, + kill_current_form: function() { + _.each(this.getChildren(), function(el) { + el.destroy(); + }); + this.fields = {}; + this.fields_order = []; + this.default_focus_field = null; + this.default_focus_button = null; + this.translatable_fields = []; + this.$element.find('.oe_form_container').empty(); }, - do_load_state: function(state, warm) { if (state.id && this.datarecord.id != state.id) { if (!this.dataset.get_id_index(state.id)) { @@ -147,9 +188,18 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } } }, - do_show: function () { var self = this; + if (this.sidebar) { + this.sidebar.$element.show(); + } + if (this.$buttons) { + this.$buttons.show(); + this.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty'); + } + if (this.$pager) { + this.$pager.show(); + } this.$element.show().css('visibility', 'hidden'); this.$element.removeClass('oe_form_dirty'); return this.has_been_loaded.pipe(function() { @@ -165,17 +215,20 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# result.pipe(function() { self.$element.css('visibility', 'visible'); }); - if (self.sidebar) { - self.sidebar.$element.show(); - } return result; }); }, do_hide: function () { - this._super(); if (this.sidebar) { this.sidebar.$element.hide(); } + if (this.$buttons) { + this.$buttons.hide(); + } + if (this.$pager) { + this.$pager.hide(); + } + this._super(); }, on_record_loaded: function(record) { var self = this, set_values = []; @@ -185,13 +238,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } this.datarecord = record; + if (this.qweb) { + this.kill_current_form(); + this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record)); + var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container'); + this.rendering_engine.render_to($dest); + } + _(this.fields).each(function (field, f) { - field.reset(); + field._dirty_flag = false; var result = field.set_value(self.datarecord[f] || false); set_values.push(result); - $.when(result).then(function() { - field.validate(); - }); }); return $.when.apply(null, set_values).pipe(function() { if (!record.id) { @@ -200,7 +257,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# _.each(self.fields_order, function(field_name) { if (record[field_name] !== undefined) { var field = self.fields[field_name]; - field.dirty = true; + field._dirty_flag = true; self.do_onchange(field); } }); @@ -209,7 +266,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# self.is_initialized.resolve(); self.do_update_pager(record.id == null); if (self.sidebar) { - self.sidebar.attachments.do_update(); + self.sidebar.do_attachement_update(self.dataset, self.datarecord.id); } if (self.default_focus_field) { self.default_focus_field.focus(); @@ -218,20 +275,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# self.do_push_state({id:record.id}); } self.$element.removeClass('oe_form_dirty'); + self.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty'); }); }, on_form_changed: function() { - for (var w in this.widgets) { - w = this.widgets[w]; - w.process_modifiers(); - if (w.field) { - w.validate(); - } - w.update_dom(); - } + this.trigger("view_content_has_changed"); }, do_notify_change: function() { this.$element.addClass('oe_form_dirty'); + this.$buttons.find('.oe_form_button_save').addClass('oe_form_button_save_dirty'); }, on_pager_action: function(action) { if (this.can_be_discarded()) { @@ -253,11 +305,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }, do_update_pager: function(hide_index) { - var $pager = this.$form_header.find('div.oe_form_pager'); var index = hide_index ? '-' : this.dataset.index + 1; - $pager.find('button').prop('disabled', this.dataset.ids.length < 2); - $pager.find('span.oe_pager_index').html(index); - $pager.find('span.oe_pager_count').html(this.dataset.ids.length); + this.$pager.find('button').prop('disabled', this.dataset.ids.length < 2).end() + .find('span.oe_pager_index').html(index).end() + .find('span.oe_pager_count').html(this.dataset.ids.length); }, parse_on_change: function (on_change, widget) { var self = this; @@ -278,7 +329,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# 'None': function () {return null;}, 'context': function (i) { context_index = i; - var ctx = new openerp.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {}); + var ctx = new instance.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {}); return ctx; } }; @@ -296,8 +347,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } // form field if (self.fields[field]) { - var value = self.fields[field].get_on_change_value(); - return value == null ? false : value; + var value_ = self.fields[field].get_value(); + return value_ == null ? false : value_; } // parent field var splitted = field.split('.'); @@ -360,17 +411,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } if (widget.field['change_default']) { - var fieldname = widget.name, value; + var fieldname = widget.name, value_; if (response.value && (fieldname in response.value)) { // Use value from onchange if onchange executed - value = response.value[fieldname]; + value_ = response.value[fieldname]; } else { // otherwise get form value for field - value = self.fields[fieldname].get_on_change_value(); + value_ = self.fields[fieldname].get_value(); } - var condition = fieldname + '=' + value; + var condition = fieldname + '=' + value_; - if (value) { + if (value_) { can_process_onchange = self.rpc({ url: '/web/dataset/call', async: false @@ -411,10 +462,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# var field = this.fields[f]; // If field is not defined in the view, just ignore it if (field) { - var value = result.value[f]; - if (field.get_value() != value) { - field.set_value(value); - field.dirty = true; + var value_ = result.value[f]; + if (field.get_value() != value_) { + field.set_value(value_); + field._dirty_flag = true; if (!_.contains(processed, field.name)) { this.do_onchange(field, processed); } @@ -424,7 +475,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.on_form_changed(); } if (!_.isEmpty(result.warning)) { - openerp.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), { + instance.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), { modal: true, buttons: [ {text: _t("Ok"), click: function() { $(this).dialog("close"); }} @@ -447,19 +498,38 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# return $.Deferred().reject(); } }, + switch_mode: function() { + var mode = this.get("mode"); + var self = this; + if(mode=="view") { + self.$buttons.find('.oe_form_buttons_edit').hide(); + self.$buttons.find('.oe_form_buttons_view').show(); + _.each(this.fields,function(field){ + field.set({"force_readonly": true}); + }); + } else { + self.$buttons.find('.oe_form_buttons_edit').show(); + self.$buttons.find('.oe_form_buttons_view').hide(); + _.each(this.fields,function(field){ + field.set({"force_readonly": false}); + }); + } + self.mode = mode; + }, on_button_save: function() { var self = this; return this.do_save().then(function(result) { - self.do_prev_view({'created': result.created, 'default': 'page'}); + self.set({mode: "view"}); }); }, on_button_cancel: function() { if (this.can_be_discarded()) { - return this.do_prev_view({'default': 'page'}); + this.set({mode: "view"}); } }, on_button_new: function() { var self = this; + this.set({mode: "edit"}); var def = $.Deferred(); $.when(this.has_been_loaded).then(function() { if (self.can_be_discarded()) { @@ -477,6 +547,44 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }); return def.promise(); }, + on_button_edit: function() { + return this.set({mode: "edit"}); + }, + on_button_create: function() { + this.dataset.index = null; + this.do_show(); + }, + on_button_duplicate: function() { + var self = this; + var def = $.Deferred(); + $.when(this.has_been_loaded).then(function() { + self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { + return self.on_created({ result : new_id }); + }).then(function() { + return self.set({mode: "edit"}); + }).then(function() { + def.resolve(); + }); + }); + return def.promise(); + }, + on_button_delete: function() { + var self = this; + var def = $.Deferred(); + $.when(this.has_been_loaded).then(function() { + if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) { + self.dataset.unlink([self.datarecord.id]).then(function() { + self.on_pager_action('next'); + def.resolve(); + }); + } else { + $.async_when().then(function () { + def.reject(); + }) + } + }); + return def.promise(); + }, can_be_discarded: function() { return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded.")); }, @@ -499,11 +607,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# f = self.fields[f]; if (!f.is_valid()) { form_invalid = true; - f.update_dom(true); if (!first_invalid_field) { first_invalid_field = f; } - } else if (f.name !== 'id' && !f.readonly && (!self.datarecord.id || f.is_dirty())) { + } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) { // Special case 'id' field, do not save this field // on 'create' : save all non readonly fields // on 'edit' : save non readonly modified fields @@ -511,20 +618,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } } if (form_invalid) { + self.set({'display_invalid_fields': true}); first_invalid_field.focus(); self.on_invalid(); return $.Deferred().reject(); } else { + self.set({'display_invalid_fields': false}); var save_deferral; if (!self.datarecord.id) { //console.log("FormView(", self, ") : About to create", values); save_deferral = self.dataset.create(values).pipe(function(r) { return self.on_created(r, undefined, prepend_on_create); }, null); - } else if (_.isEmpty(values)) { + } else if (_.isEmpty(values) && ! self.force_dirty) { //console.log("FormView(", self, ") : Nothing to save"); save_deferral = $.Deferred().resolve({}).promise(); } else { + self.force_dirty = false; //console.log("FormView(", self, ") : About to save", values); save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) { return self.on_saved(r); @@ -542,7 +652,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# var msg = "<ul>"; _.each(this.fields, function(f) { if (!f.is_valid()) { - msg += "<li>" + f.string + "</li>"; + msg += "<li>" + f.node.attrs.string + "</li>"; } }); msg += "</ul>"; @@ -580,14 +690,14 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id])); this.dataset.index = this.dataset.ids.length - 1; } else { - this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids)); + this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids)); this.dataset.index = 0; } this.do_update_pager(); if (this.sidebar) { - this.sidebar.attachments.do_update(); + this.sidebar.do_attachement_update(this.dataset, this.datarecord.id); } - //openerp.log("The record has been created with id #" + this.datarecord.id); + //instance.log("The record has been created with id #" + this.datarecord.id); this.reload(); return $.when(_.extend(r, {created: true})).then(success); } @@ -598,6 +708,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# reload: function() { var self = this; return this.reload_mutex.exec(function() { + if (self.dataset.index == null) { + self.do_prev_view(); + return $.Deferred().reject().promise(); + } if (self.dataset.index == null || self.dataset.index < 0) { return $.when(self.on_button_new()); } else { @@ -607,15 +721,20 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }); }, + get_widgets: function() { + return _.filter(this.getChildren(), function(obj) { + return obj instanceof instance.web.form.Widget; + }); + }, get_fields_values: function(blacklist) { blacklist = blacklist || []; var values = {}; var ids = this.get_selected_ids(); values["id"] = ids.length > 0 ? ids[0] : false; - _.each(this.fields, function(value, key) { + _.each(this.fields, function(value_, key) { if (_.include(blacklist, key)) return; - var val = value.get_value(); + var val = value_.get_value(); values[key] = val; }); return values; @@ -632,8 +751,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }); }, is_dirty: function() { - return _.any(this.fields, function (value) { - return value.is_dirty(); + return _.any(this.fields, function (value_) { + return value_._dirty_flag; }); }, is_interactible_record: function() { @@ -642,7 +761,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (this.options.not_interactible_on_create) return false; } else if (typeof(id) === "string") { - if(openerp.web.BufferedDataSet.virtual_id_regex.test(id)) + if(instance.web.BufferedDataSet.virtual_id_regex.test(id)) return false; } return true; @@ -654,12 +773,12 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# var self = this; var fields = _.chain(this.fields) .map(function (field, name) { - var value = field.get_value(); + var value_ = field.get_value(); // ignore fields which are empty, invisible, readonly, o2m // or m2m - if (!value - || field.invisible - || field.readonly + if (!value_ + || field.get('invisible') + || field.get("readonly") || field.field.type === 'one2many' || field.field.type === 'many2many') { return false; @@ -668,33 +787,33 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# switch(field.field.type) { case 'selection': displayed = _(field.values).find(function (option) { - return option[0] === value; + return option[0] === value_; })[1]; break; case 'many2one': - displayed = field.value[1] || value; + displayed = value_; break; default: - displayed = value; + displayed = value_; } return { name: name, - string: field.string, - value: value, + string: field.node_atts.string, + value: value_, displayed: displayed, // convert undefined to false change_default: !!field.field.change_default } }) .compact() - .sortBy(function (field) { return field.string; }) + .sortBy(function (field) { return field.node_atts.string; }) .value(); var conditions = _.chain(fields) .filter(function (field) { return field.change_default; }) .value(); - var d = new openerp.web.Dialog(this, { + var d = new instance.web.Dialog(this, { title: _t("Set Default"), args: { fields: fields, @@ -706,12 +825,12 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# var $defaults = d.$element.find('#formview_default_fields'); var field_to_set = $defaults.val(); if (!field_to_set) { - $defaults.parent().addClass('invalid'); + $defaults.parent().addClass('oe_form_invalid'); return; } var condition = d.$element.find('#formview_default_conditions').val(), all_users = d.$element.find('#formview_default_all').is(':checked'); - new openerp.web.DataSet(self, 'ir.values').call( + new instance.web.DataSet(self, 'ir.values').call( 'set_default', [ self.dataset.model, field_to_set, @@ -725,9 +844,368 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }); d.template = 'FormView.set_default'; d.open(); - } + }, + register_field: function(field, name) { + this.fields[name] = field; + this.fields_order.push(name); + if (this.get_field(name).translate) { + this.translatable_fields.push(field); + } + field.on('changed_value', this, function() { + field._dirty_flag = true; + if (field.is_syntax_valid()) { + this.do_onchange(field); + this.on_form_changed(true); + this.do_notify_change(); + } + }); + }, + get_field: function(field_name) { + return this.fields_view.fields[field_name]; + }, + is_create_mode: function() { + return !this.datarecord.id; + }, }); -openerp.web.FormDialog = openerp.web.Dialog.extend({ + +/** + * Interface to be implemented by rendering engines for the form view. + */ +instance.web.form.FormRenderingEngineInterface = { + set_fields_view: function(fields_view) {}, + set_fields_registry: function(fields_registry) {}, + render_to: function($element) {}, +}; + +/** + * Default rendering engine for the form view. + * + * It is necessary to set the view using set_view() before usage. + */ +instance.web.form.FormRenderingEngine = instance.web.Class.extend({ + init: function(view) { + this.view = view; + }, + set_fields_view: function(fvg) { + this.fvg = fvg; + }, + set_tags_registry: function(tags_registry) { + this.tags_registry = tags_registry; + }, + set_fields_registry: function(fields_registry) { + this.fields_registry = fields_registry; + }, + render_to: function($target) { + var self = this; + this.$target = $target; + + // TODO: I know this will save the world and all the kitten for a moment, + // but one day, we will have to get rid of xml2json + var xml = instance.web.json_node_to_xml(this.fvg.arch); + this.$form = $('<div class="oe_form">' + xml + '</div>'); + if (this.fvg.arch.attrs && this.fvg.arch.attrs['layout'] !== 'manual') { + this.$form.attr('layout', 'auto'); + } + + this.fields_to_init = []; + this.tags_to_init = []; + this.labels = {}; + this.process(this.$form); + + this.$form.appendTo(this.$target); + + _.each(this.fields_to_init, function($elem) { + var name = $elem.attr("name"); + if (!self.fvg.fields[name]) { + throw new Error("Field '" + name + "' specified in view could not be found."); + } + var obj = self.fields_registry.get_any([$elem.attr('widget'), self.fvg.fields[name].type]); + if (!obj) { + throw new Error("Widget type '"+ key + "' is not implemented"); + } + var w = new (obj)(self.view, instance.web.xml_to_json($elem[0])); + var $label = self.labels[$elem.attr("name")]; + if ($label) { + w.set_input_id($label.attr("for")); + } + self.alter_field(w); + self.view.register_field(w, $elem.attr("name")); + w.replace($elem); + }); + _.each(this.tags_to_init, function($elem) { + var tag_name = $elem[0].tagName.toLowerCase(); + var obj = self.tags_registry.get_object(tag_name); + var w = new (obj)(self.view, instance.web.xml_to_json($elem[0])); + w.replace($elem); + }) + // TODO: return a deferred + }, + render_element: function(template, layout/* dictionaries */) { + var dicts = [].slice.call(arguments).slice(2); + dicts.unshift({ 'layout' : layout }); + var dict = _.extend.apply(_, dicts); + dict['classnames'] = dict['class'] || ''; // class is a reserved word and might caused problem to Safari when used from QWeb + var alt_template = template + '.' + layout; + template = QWeb.has_template(alt_template) ? alt_template : template; + return $(QWeb.render(template, dict)); + }, + alter_field: function(field) { + }, + toggle_layout_debugging: function() { + if (!this.$target.has('.oe_layout_debug_cell:first').length) { + this.$target.find('[title]').removeAttr('title'); + this.$target.find('.oe_form_group_cell').each(function() { + var text = 'W:' + ($(this).attr('width') || '') + ' - C:' + $(this).attr('colspan'); + $(this).attr('title', text); + }); + } + this.$target.toggleClass('oe_layout_debugging'); + }, + process: function($tag, layout) { + var self = this; + if ($tag.attr('layout') === 'auto') { + $tag.addClass('oe_form_autolayout'); + } + layout = $tag.attr('layout') || layout || 'auto'; + $tag.removeAttr('layout'); + var tagname = $tag[0].nodeName.toLowerCase(); + if (this.tags_registry.contains(tagname)) { + this.tags_to_init.push($tag); + return $tag; + } + var fn = self['process_' + tagname]; + if (fn) { + var args = [].slice.call(arguments); + args[0] = $tag; + args[1] = layout; + return fn.apply(self, args); + } else { + // generic tag handling, just process children + $tag.children().each(function() { + self.process($(this), layout); + }); + self.handle_common_properties($tag, $tag); + $tag.removeAttr("modifiers"); + return $tag; + } + }, + process_sheet: function() { + this.process_form.apply(this, arguments); + }, + process_form: function($form, layout) { + var $new_form = this.render_element('FormRenderingForm', layout, $form.getAttributes()); + var $dst = (layout === 'auto') ? $new_form.find('group:first') : $new_form; + $new_form.attr("modifiers", $form.attr("modifiers")); + $form.children().appendTo($dst); + if ($form[0] === this.$form[0]) { + // If root element, replace it + this.$form = $new_form; + } else { + $form.before($new_form).remove(); + } + this.process($new_form); + }, + preprocess_field: function($field) { + var self = this; + var name = $field.attr('name'), + field_colspan = parseInt($field.attr('colspan'), 10), + field_modifiers = JSON.parse($field.attr('modifiers') || '{}'); + + if ($field.attr('nolabel') === '1') + return; + $field.attr('nolabel', '1'); + var found = false; + this.$form.find('label[for="' + name + '"]').each(function(i ,el) { + $(el).parents().each(function(unused, tag) { + var name = tag.tagName.toLowerCase(); + if (name === "field" || name in self.tags_registry.map) + found = true; + }); + }); + if (found) + return; + + $label = $('<label/>').attr({ + 'for' : name, + "modifiers": JSON.stringify({invisible: field_modifiers.invisible}), + "string": $field.attr('string'), + "help": $field.attr('help'), + }); + $label.insertBefore($field); + if (field_colspan > 1) { + $field.attr('colspan', field_colspan - 1); + } + return $label; + }, + process_field: function($field, layout) { + var $label = this.preprocess_field($field); + if ($label) + this.process($label, layout); + this.fields_to_init.push($field); + return $field; + }, + process_group: function($group, layout) { + var self = this; + if ($group.parent().is('.oe_form_group_cell')) { + $group.parent().addClass('oe_form_group_nested'); + } + $group.children('field').each(function() { + self.preprocess_field($(this)); + }); + var $new_group = this.render_element('FormRenderingGroup', layout, $group.getAttributes()), + $table; + if ($new_group.is('table')) { + $table = $new_group; + } else { + $table = $new_group.find('table:first'); + } + $table.addClass('oe_form_group'); + var $tr, $td, + cols = parseInt($group.attr('col') || 4, 10), + row_cols = cols; + + var children = []; + $group.children().each(function(a,b,c) { + var $child = $(this), + colspan = parseInt($child.attr('colspan') || 1, 10), + tagName = $child[0].tagName.toLowerCase(); + if (tagName === 'newline') { + $tr = null; + return; + } + if (!$tr || row_cols < colspan) { + $tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table); + row_cols = cols; + } + row_cols -= colspan; + + $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan); + // invisibility transfer + var field_modifiers = JSON.parse($child.attr('modifiers') || '{}'); + var invisible = field_modifiers.invisible; + field_modifiers.invisible = undefined; + $child.attr('modifiers', JSON.stringify(field_modifiers)); + self.handle_common_properties($td, $("<dummy>").attr("modifiers", JSON.stringify({invisible: invisible}))); + + $tr.append($td.append($child)); + children.push($child[0]); + }); + if (row_cols && $td) { + $td.attr('colspan', parseInt($td.attr('colspan'), 10) + row_cols); + } + $group.before($new_group).remove(); + + // Now compute width of cells + $table.find('> tbody > tr').each(function() { + var to_compute = [], + row_cols = cols, + total = 100; + $(this).children().each(function() { + var $td = $(this), + $child = $td.children(':first'); + switch ($child[0].tagName.toLowerCase()) { + case 'separator': + if ($child.attr('orientation') === 'vertical') { + $td.addClass('oe_vertical_separator').attr('width', '1'); + $td.empty(); + row_cols--; + } + break; + case 'label': + if ($child.attr('for')) { + $td.attr('width', '1%'); + row_cols--; + total--; + } + break; + default: + to_compute.push($td); + } + }); + var unit = Math.floor(total / row_cols); + _.each(to_compute, function($td, i) { + var width = parseInt($td.attr('colspan'), 10) * unit; + $td.attr('width', ((i == to_compute.length - 1) ? total : width) + '%'); + total -= width; + }); + }); + _.each(children, function(el) { + self.process($(el)); + }); + this.handle_common_properties($new_group, $group); + return $new_group; + }, + process_notebook: function($notebook, layout) { + var self = this; + var pages = []; + $notebook.find('> page').each(function() { + var $page = $(this); + var page_attrs = $page.getAttributes(); + page_attrs.id = _.uniqueId('notebook_page_'); + pages.push(page_attrs); + var $new_page = self.render_element('FormRenderingNotebookPage', layout, page_attrs); + var $dst = (layout === 'auto') ? $new_page.find('group:first') : $new_page; + $page.children().appendTo($dst); + $page.before($new_page).remove(); + self.handle_common_properties($new_page, $page); + }); + var $new_notebook = this.render_element('FormRenderingNotebook', layout, { pages : pages }); + $notebook.children().appendTo($new_notebook); + $notebook.before($new_notebook).remove(); + $new_notebook.children().each(function() { + self.process($(this)); + }); + $new_notebook.tabs(); + this.handle_common_properties($new_notebook, $notebook); + return $new_notebook; + }, + process_separator: function($separator, layout) { + var $new_separator = this.render_element('FormRenderingSeparator', layout, $separator.getAttributes()); + $separator.before($new_separator).remove(); + this.handle_common_properties($new_separator, $separator); + return $new_separator; + }, + process_label: function($label, layout) { + var name = $label.attr("for"), + field_orm = this.fvg.fields[name]; + var dict = { + string: $label.attr('string') || (field_orm || {}).string || '', + help: $label.attr('help') || (field_orm || {}).help || '', + _for: name ? _.uniqueId('oe-field-input-') : undefined, + }; + var align = parseFloat(dict.align); + if (isNaN(align) || align === 1) { + align = 'right'; + } else if (align === 0) { + align = 'left'; + } else { + align = 'center'; + } + dict.align = align; + var $new_label = this.render_element('FormRenderingLabel', layout, dict); + $label.before($new_label).remove(); + this.handle_common_properties($new_label, $label); + if (name) { + this.labels[name] = $new_label; + } + return $new_label; + }, + handle_common_properties: function($new_element, $node) { + var str_modifiers = $node.attr("modifiers") || "{}" + var modifiers = JSON.parse(str_modifiers); + if (modifiers.invisible !== undefined) + new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element); + $new_element.addClass($node.attr("class") || ""); + }, +}); + +instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({ + alter_field: function(field) { + field.set({"force_readonly": true}); + }, +}); + +instance.web.form.FormDialog = instance.web.Dialog.extend({ init: function(parent, options, view_id, dataset) { this._super(parent, options); this.dataset = dataset; @@ -736,8 +1214,7 @@ openerp.web.FormDialog = openerp.web.Dialog.extend({ }, start: function() { this._super(); - this.form = new openerp.web.FormView(this, this.dataset, this.view_id, { - sidebar: false, + this.form = new instance.web.FormView(this, this.dataset, this.view_id, { pager: false }); this.form.appendTo(this.$element); @@ -758,62 +1235,7 @@ openerp.web.FormDialog = openerp.web.Dialog.extend({ } }); -/** @namespace */ -openerp.web.form = {}; - -openerp.web.form.SidebarAttachments = openerp.web.OldWidget.extend({ - init: function(parent, form_view) { - var $section = parent.add_section(_t('Attachments'), 'attachments'); - this.$div = $('<div class="oe-sidebar-attachments"></div>'); - $section.append(this.$div); - - this._super(parent, $section.attr('id')); - this.view = form_view; - }, - do_update: function() { - if (!this.view.datarecord.id) { - this.on_attachments_loaded([]); - } else { - (new openerp.web.DataSetSearch( - this, 'ir.attachment', this.view.dataset.get_context(), - [ - ['res_model', '=', this.view.dataset.model], - ['res_id', '=', this.view.datarecord.id], - ['type', 'in', ['binary', 'url']] - ])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded); - } - }, - on_attachments_loaded: function(attachments) { - this.attachments = attachments; - this.$div.html(QWeb.render('FormView.sidebar.attachments', this)); - this.$element.find('.oe-binary-file').change(this.on_attachment_changed); - this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete); - }, - on_attachment_changed: function(e) { - window[this.element_id + '_iframe'] = this.do_update; - var $e = $(e.target); - if ($e.val() != '') { - this.$element.find('form.oe-binary-form').submit(); - $e.parent().find('input[type=file]').prop('disabled', true); - $e.parent().find('button').prop('disabled', true).find('img, span').toggle(); - } - }, - on_attachment_delete: function(e) { - var self = this, $e = $(e.currentTarget); - var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text()); - if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) { - this.rpc('/web/dataset/unlink', { - model: 'ir.attachment', - ids: [parseInt($e.attr('data-id'))] - }, function(r) { - $e.parent().remove(); - self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted"); - }); - } - } -}); - -openerp.web.form.compute_domain = function(expr, fields) { +instance.web.form.compute_domain = function(expr, fields) { var stack = []; for (var i = expr.length - 1; i >= 0; i--) { var ex = expr[i]; @@ -842,7 +1264,7 @@ openerp.web.form.compute_domain = function(expr, fields) { _t("Unknown field %s in domain %s"), ex[0], JSON.stringify(expr))); } - var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value; + var field_value = field.get_value ? field.get_value() : field.value; var op = ex[1]; var val = ex[2]; @@ -884,60 +1306,84 @@ openerp.web.form.compute_domain = function(expr, fields) { return _.all(stack, _.identity); }; -openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{ - form_template: 'Widget', +/** + * Must be applied over an class already possessing the GetterSetterMixin. + * + * Apply the result of the "invisible" domain to this.$element. + */ +instance.web.form.InvisibilityChangerMixin = { + init: function(field_manager, invisible_domain) { + this._ic_field_manager = field_manager + this._ic_invisible_modifier = invisible_domain; + this._ic_field_manager.on("view_content_has_changed", this, function() { + var result = this._ic_invisible_modifier === undefined ? false : + instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields); + this.set({"invisible": result}); + }); + this.set({invisible: this._ic_invisible_modifier === true}); + }, + start: function() { + var check_visibility = function() { + if (this.get("invisible")) { + this.$element.hide(); + } else { + this.$element.show(); + } + }; + this.on("change:invisible", this, check_visibility); + _.bind(check_visibility, this)(); + }, +}; + +instance.web.form.InvisibilityChanger = instance.web.Class.extend(_.extend({}, instance.web.GetterSetterMixin, instance.web.form.InvisibilityChangerMixin, { + init: function(parent, field_manager, invisible_domain, $element) { + this.setParent(parent); + instance.web.GetterSetterMixin.init.call(this); + instance.web.form.InvisibilityChangerMixin.init.call(this, field_manager, invisible_domain); + this.$element = $element; + this.start(); + }, +})); + +instance.web.form.Widget = instance.web.Widget.extend(_.extend({}, instance.web.form.InvisibilityChangerMixin, { /** - * @constructs openerp.web.form.Widget - * @extends openerp.web.OldWidget + * @constructs instance.web.form.Widget + * @extends instance.web.Widget * * @param view * @param node */ init: function(view, node) { + this._super(view); this.view = view; this.node = node; this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}'); - this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true); - this.type = this.type || node.tag; - this.element_name = this.element_name || this.type; - - this._super(view); - - this.children = node.children; - this.colspan = parseInt(node.attrs.colspan || 1, 10); - this.decrease_max_width = 0; - - this.string = this.string || node.attrs.string; - this.help = this.help || node.attrs.help; - this.invisible = this.modifiers['invisible'] === true; - this.classname = 'oe_form_' + this.type; - - this.align = parseFloat(this.node.attrs.align); - if (isNaN(this.align) || this.align === 1) { - this.align = 'right'; - } else if (this.align === 0) { - this.align = 'left'; - } else { - this.align = 'center'; - } + instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible); - this.width = this.node.attrs.width; + this.view.on("view_content_has_changed", this, this.process_modifiers); + }, + renderElement: function() { + this._super(); + this.$element.addClass(this.node.attrs["class"] || ""); + }, + start: function() { + this._super(); + instance.web.form.InvisibilityChangerMixin.start.call(this); }, destroy: function() { - this._super.apply(this, arguments); $.fn.tipsy.clear(); + this._super.apply(this, arguments); }, process_modifiers: function() { - var compute_domain = openerp.web.form.compute_domain; + var compute_domain = instance.web.form.compute_domain; + var to_set = {}; for (var a in this.modifiers) { - this[a] = compute_domain(this.modifiers[a], this.view.fields); + if (!_.include(["invisible"], a)) { + var val = compute_domain(this.modifiers[a], this.view.fields); + to_set[a] = val; + } } - }, - update_dom: function() { - this.$element.toggle(!this.invisible); - }, - renderElement: function() { - this.$element.html(QWeb.render(this.form_template, { "widget": this })); + this.set(to_set); }, do_attach_tooltip: function(widget, trigger, options) { widget = widget || this; @@ -947,12 +1393,12 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo delayOut: 0, fade: true, title: function() { - var template = widget.form_template + '.tooltip'; + var template = widget.template + '.tooltip'; if (!QWeb.has_template(template)) { template = 'WidgetLabel.tooltip'; } return QWeb.render(template, { - debug: openerp.connection.debug, + debug: instance.connection.debug, widget: widget })}, gravity: $.fn.tipsy.autoBounds(50, 'nw'), @@ -979,7 +1425,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo }, _build_eval_context: function(blacklist) { var a_dataset = this.view.dataset; - return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); + return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); }, /** * Builds a new context usable for operations related to fields by merging @@ -994,7 +1440,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo if (v_context.__ref || true) { //TODO: remove true var fields_values = this._build_eval_context(blacklist); - v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values); + v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values); } return v_context; }, @@ -1005,235 +1451,28 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo var final_domain = n_domain !== null ? n_domain : f_domain; if (!(final_domain instanceof Array) || true) { //TODO: remove true var fields_values = this._build_eval_context(); - final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values); + final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values); } return final_domain; } -}); +})); -openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({ - form_template: 'WidgetFrame', - init: function(view, node) { - this._super(view, node); - this.columns = parseInt(node.attrs.col || 4, 10); - this.x = 0; - this.y = 0; - this.table = []; - this.add_row(); - for (var i = 0; i < node.children.length; i++) { - var n = node.children[i]; - if (n.tag == "newline") { - this.add_row(); - } else { - this.handle_node(n); - } - } - this.set_row_cells_with(this.table[this.table.length - 1]); - }, - renderElement: function() { - this._super(); - var self = this; - _.each(this.table, function(row) { - _.each(row, function(td) { - td.$element = self.$element.find('.' + td.element_class); - td.renderElement(); - }); - }); - }, - start: function() { - _.each(this.table, function(row) { - _.each(row, function(td) { - td.start(); - }); - }); - }, - add_row: function(){ - if (this.table.length) { - this.set_row_cells_with(this.table[this.table.length - 1]); - } - var row = []; - this.table.push(row); - this.x = 0; - this.y += 1; - return row; - }, - set_row_cells_with: function(row) { - var bypass = 0, - max_width = 100, - row_length = row.length; - for (var i = 0; i < row.length; i++) { - if (row[i].always_invisible) { - row_length--; - } else { - bypass += row[i].width === undefined ? 0 : 1; - max_width -= row[i].decrease_max_width; - } - } - var size_unit = Math.round(max_width / (this.columns - bypass)), - colspan_sum = 0; - for (var i = 0; i < row.length; i++) { - var w = row[i]; - if (w.always_invisible) { - continue; - } - colspan_sum += w.colspan; - if (w.width === undefined) { - var width = (i === row_length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan); - max_width -= width; - w.width = width + '%'; - } - } - }, - handle_node: function(node) { - var type = {}; - if (node.tag == 'field') { - type = this.view.fields_view.fields[node.attrs.name] || {}; - if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') { - // This way we can retain backward compatibility between addons and old clients - node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1; - node.attrs.nolabel = '1'; - } - } - var widget = instanciate_widget(this.view.registry.get_any( - [node.attrs.widget, type.type, node.tag]), this.view, node); - if (node.tag == 'field') { - if (!this.view.default_focus_field || node.attrs.default_focus == '1') { - this.view.default_focus_field = widget; - } - if (node.attrs.nolabel != '1') { - var label = instanciate_widget(this.view.registry.get_object('label'), this.view, node); - label["for"] = widget; - this.add_widget(label, widget.colspan + 1); - } - } - this.add_widget(widget); - }, - add_widget: function(widget, colspan) { - var current_row = this.table[this.table.length - 1]; - if (!widget.always_invisible) { - colspan = colspan || widget.colspan; - if (current_row.length && (this.x + colspan) > this.columns) { - current_row = this.add_row(); - } - this.x += widget.colspan; - } - current_row.push(widget); - return widget; - } -}); - -openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({ - form_template: 'WidgetGroup' -}), - -openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({ - form_template: 'WidgetNotebook', - init: function(view, node) { - this._super(view, node); - this.pages = []; - for (var i = 0; i < node.children.length; i++) { - var n = node.children[i]; - if (n.tag == "page") { - var page = instanciate_widget(this.view.registry.get_object('notebookpage'), - this.view, n, this, this.pages.length); - this.pages.push(page); - } - } - }, - renderElement: function() { - this._super(); - var self = this; - _.each(this.pages, function(page) { - page.$element = self.$element.find('.' + page.element_class); - page.renderElement(); - }); - }, - start: function() { - var self = this; - this._super.apply(this, arguments); - this.$element.find('> ul > li').each(function (index, tab_li) { - var page = self.pages[index], - id = _.uniqueId(self.element_name + '-'); - page.element_id = id; - $(tab_li).find('a').attr('href', '#' + id); - }); - this.$element.find('> div').each(function (index, page) { - page.id = self.pages[index].element_id; - }); - this.$element.tabs(); - this.view.on_button_new.add_first(this.do_select_first_visible_tab); - if (openerp.connection.debug) { - this.do_attach_tooltip(this, this.$element.find('ul:first'), { - gravity: 's' - }); - } - _.each(this.pages, function(page) { - page.start(); - }); - }, - do_select_first_visible_tab: function() { - for (var i = 0; i < this.pages.length; i++) { - var page = this.pages[i]; - if (page.invisible === false) { - this.$element.tabs('select', page.index); - break; - } - } - } -}); - -openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({ - form_template: 'WidgetNotebookPage', - init: function(view, node, notebook, index) { - this.notebook = notebook; - this.index = index; - this.element_name = 'page_' + index; - this._super(view, node); - }, - start: function() { - this._super.apply(this, arguments); - this.$element_tab = this.notebook.$element.find( - '> ul > li:eq(' + this.index + ')'); - }, - update_dom: function() { - if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) { - this.notebook.do_select_first_visible_tab(); - } - this.$element_tab.toggle(!this.invisible); - this.$element.toggle(!this.invisible); - } -}); - -openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({ - form_template: 'WidgetSeparator', - init: function(view, node) { - this._super(view, node); - this.orientation = node.attrs.orientation || 'horizontal'; - if (this.orientation === 'vertical') { - this.width = '1'; - } - this.classname += '_' + this.orientation; - } -}); - -openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ - form_template: 'WidgetButton', +instance.web.form.WidgetButton = instance.web.form.Widget.extend({ + template: 'WidgetButton', init: function(view, node) { this._super(view, node); this.force_disabled = false; - if (this.string) { - // We don't have button key bindings in the webclient - this.string = this.string.replace(/_/g, ''); - } - if (node.attrs.default_focus == '1') { + this.string = (this.node.attrs.string || '').replace(/_/g, ''); + if (this.node.attrs.default_focus == '1') { // TODO fme: provide enter key binding to widgets this.view.default_focus_button = this; } + this.view.on('view_content_has_changed', this, this.check_disable); }, start: function() { this._super.apply(this, arguments); - this.$element.find("button").click(this.on_click); - if (this.help || openerp.connection.debug) { + this.$element.click(this.on_click); + if (this.node.attrs.help || instance.connection.debug) { this.do_attach_tooltip(); } }, @@ -1251,7 +1490,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ var exec_action = function() { if (self.node.attrs.confirm) { var def = $.Deferred(); - var dialog = openerp.web.dialog($('<div>' + self.node.attrs.confirm + '</div>'), { + var dialog = instance.web.dialog($('<div/>').text(self.node.attrs.confirm), { title: _t('Confirm'), modal: true, buttons: [ @@ -1275,6 +1514,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ } }; if (!this.node.attrs.special) { + this.view.force_dirty = true; return this.view.recursive_save().pipe(exec_action); } else { return exec_action(); @@ -1285,7 +1525,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ var context = this.node.attrs.context; if (context && context.__ref) { - context = new openerp.web.CompoundContext(context); + context = new instance.web.CompoundContext(context); context.set_eval_context(this._build_eval_context()); } @@ -1295,81 +1535,57 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ self.view.reload(); }); }, - update_dom: function() { - this._super.apply(this, arguments); - this.check_disable(); - }, check_disable: function() { - var disabled = (this.readonly || this.force_disabled || !this.view.is_interactible_record()); - this.$element.find('button').prop('disabled', disabled); - this.$element.find("button").css('color', disabled ? 'grey' : ''); + var disabled = (this.force_disabled || !this.view.is_interactible_record()); + this.$element.prop('disabled', disabled); + this.$element.css('color', disabled ? 'grey' : ''); } }); -openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({ - form_template: 'WidgetLabel', - init: function(view, node) { - this.element_name = 'label_' + node.attrs.name; - - this._super(view, node); - - if (this.node.tag == 'label' && !this.string && this.node.children.length) { - this.string = this.node.children[0]; - this.align = 'left'; - } - - if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) { - this.form_template = "WidgetParagraph"; - this.colspan = parseInt(this.node.attrs.colspan || 1, 10); - // Widgets default to right-aligned, but paragraph defaults to - // left-aligned - if (isNaN(parseFloat(this.node.attrs.align))) { - this.align = 'left'; - } - - this.multilines = this.string && _.str.lines(this.string).length > 1; - } else { - this.colspan = 1; - this.width = '1%'; - this.decrease_max_width = 1; - this.nowrap = true; - } - }, - renderElement: function() { - var rendered; - if (this['for'] && this.type !== 'label') { - rendered = QWeb.render(this.form_template, {widget: this['for']}); - } else { - // Actual label widgets should not have a false and have type label - rendered = QWeb.render(this.form_template, {widget: this}); - } - this.$element.html(rendered); - }, - start: function() { - this._super(); - var self = this; - if (this['for'] && (this['for'].help || openerp.connection.debug)) { - this.do_attach_tooltip(self['for']); - } - this.$element.find("label").dblclick(function() { - var widget = self['for'] || self; - openerp.log(widget.element_class , widget); - window.w = widget; - }); - } -}); +/** + * Interface implemented by the form view or any other object + * able to provide the features necessary for the fields to work. + * + * Properties: + * - display_invalid_fields : if true, all fields where is_valid() return true should + * be displayed as invalid. + * Events: + * - view_content_has_changed : when the values of the fields have changed. When + * this event is triggered all fields should reprocess their modifiers. + */ +instance.web.form.FieldManagerInterface = { + /** + * Must return the asked field as in fields_get. + */ + get_field: function(field_name) {}, + /** + * Called by the field when the translate button is clicked. + */ + open_translate_dialog: function(field) {}, + /** + * Returns true when the view is in create mode. + */ + is_create_mode: function() {}, +}; /** * Interface to be implemented by fields. * - * Novajs Attributes: - * - ... - * - * Novajs Events: - * - ... + * Properties: + * - readonly: boolean. If set to true the field should appear in readonly mode. + * - force_readonly: boolean, When it is true, the field should always appear + * in read only mode, no matter what the value of the "readonly" property can be. + * Events: + * - changed_value: triggered to inform the view to check on_changes * */ -openerp.web.form.FieldInterface = { +instance.web.form.FieldInterface = { + /** + * Constructor takes 2 arguments: + * - field_manager: Implements FieldManagerInterface + * - node: the "<field>" node in json form + */ + init: function(field_manager, node) {}, /** * Called by the form view to indicate the value of the field. * @@ -1386,7 +1602,7 @@ openerp.web.form.FieldInterface = { * as well as any format commonly returned in a on_change. It must be able to autodetect those formats as * no information is ever given to know which format is used. */ - set_value: function(value) {}, + set_value: function(value_) {}, /** * Get the current value of the widget. * @@ -1405,221 +1621,307 @@ openerp.web.form.FieldInterface = { * syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0). */ get_value: function() {}, + /** + * Inform the current object of the id it should use to match a html <label> that exists somewhere in the + * view. + */ + set_input_id: function(id) {}, + /** + * Returns true if is_syntax_valid() returns true and the value is semantically + * valid too according to the semantic restrictions applied to the field. + */ + is_valid: function() {}, + /** + * Returns true if the field holds a value which is syntaxically correct, ignoring + * the potential semantic restrictions applied to the field. + */ + is_syntax_valid: function() {}, + /** + * Must set the focus on the field. + */ + focus: function() {}, }; /** - * Abstract class for classes implementing FieldInterface. Should be renamed to AbstractField some - * day. + * Abstract class for classes implementing FieldInterface. + * + * Properties: + * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending + * the values of the "readonly" property and the "force_readonly" property on the field manager. + * - value: useful property to hold the value of the field. By default, set_value() and get_value() + * set and retrieve the value property. Changing the value property also triggers automatically + * a 'changed_value' event that inform the view to trigger on_changes. + * */ -openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{ +instance.web.form.AbstractField = instance.web.form.Widget.extend(/** @lends instance.web.form.AbstractField# */{ /** - * @constructs openerp.web.form.Field - * @extends openerp.web.form.Widget + * @constructs instance.web.form.AbstractField + * @extends instance.web.form.Widget * - * @param view + * @param field_manager * @param node */ - init: function(view, node) { - this.name = node.attrs.name; - this.value = undefined; - view.fields[this.name] = this; - view.fields_order.push(this.name); - this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type; - this.element_name = "field_" + this.name + "_" + this.type; - - this._super(view, node); - - if (node.attrs.nolabel != '1' && this.colspan > 1) { - this.colspan--; - } - this.field = view.fields_view.fields[node.attrs.name] || {}; - this.string = node.attrs.string || this.field.string; - this.help = node.attrs.help || this.field.help; - this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1'; - this.readonly = this.modifiers['readonly'] === true; - this.required = this.modifiers['required'] === true; - this.invalid = this.dirty = false; - - this.classname = 'oe_form_field_' + this.type; + init: function(field_manager, node) { + this._super(field_manager, node); + this.field_manager = field_manager; + this.name = this.node.attrs.name; + this.set({'value': false}); + this.field = this.field_manager.get_field(this.name); + this.set({required: this.modifiers['required'] === true}); + + // some events to make the property "effective_readonly" sync automatically with "readonly" and + // "force_readonly" + this.set({"readonly": this.modifiers['readonly'] === true}); + var test_effective_readonly = function() { + this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")}); + }; + this.on("change:readonly", this, test_effective_readonly); + this.on("change:force_readonly", this, test_effective_readonly); + _.bind(test_effective_readonly, this)(); + + this.on("change:value", this, function() { + if (! this._inhibit_on_change) + this.trigger('changed_value'); + this._check_css_flags(); + }); }, start: function() { this._super.apply(this, arguments); if (this.field.translate) { - this.view.translatable_fields.push(this); this.$element.addClass('oe_form_field_translatable'); - this.$element.find('.oe_field_translate').click(this.on_translate); + this.$element.find('.oe_field_translate').click(_.bind(function() { + this.field_manager.open_translate_dialog(this); + }, this)); } - if (this.nolabel && openerp.connection.debug) { + if (instance.connection.debug) { this.do_attach_tooltip(this, this.$element); } + if (!this.disable_utility_classes) { + var set_required = function() { + this.$element.toggleClass('oe_form_required', this.get("required")); + }; + this.on("change:required", this, set_required); + _.bind(set_required, this)(); + } }, - set_value: function(value) { - this.value = value; - this.invalid = false; - this.update_dom(); - this.on_value_changed(); - }, - set_value_from_ui: function() { - this.on_value_changed(); - }, - on_value_changed: function() { - }, - on_translate: function() { - this.view.open_translate_dialog(this); + set_value: function(value_) { + this._inhibit_on_change = true; + this.set({'value': value_}); + this._inhibit_on_change = false; }, get_value: function() { - return this.value; + return this.get('value'); }, is_valid: function() { - return !this.invalid; + return this.is_syntax_valid() && (! this.get('required') || ! this.is_false()); }, - is_dirty: function() { - return this.dirty && !this.readonly; + is_syntax_valid: function() { + return true; }, - get_on_change_value: function() { - return this.get_value(); + /** + * Method useful to implement to ease validity testing. Must return true if the current + * value is similar to false in OpenERP. + */ + is_false: function() { + return this.get('value') === false; }, - update_dom: function(show_invalid) { - this._super.apply(this, arguments); + _check_css_flags: function(show_invalid) { if (this.field.translate) { - this.$element.find('.oe_field_translate').toggle(!!this.view.datarecord.id); + this.$element.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode()); } if (!this.disable_utility_classes) { - this.$element.toggleClass('disabled', this.readonly); - this.$element.toggleClass('required', this.required); - if (show_invalid) { - this.$element.toggleClass('invalid', !this.is_valid()); + if (this.field_manager.get('display_invalid_fields')) { + this.$element.toggleClass('oe_form_invalid', !this.is_valid()); } } }, - on_ui_change: function() { - this.dirty = true; - this.validate(); - if (this.is_valid()) { - this.set_value_from_ui(); - this.view.do_onchange(this); - this.view.on_form_changed(true); - this.view.do_notify_change(); - } else { - this.update_dom(true); - } - }, - validate: function() { - this.invalid = false; + focus: function() { }, - focus: function($element) { - if ($element) { - setTimeout(function() { - $element.focus(); - }, 50); - } - }, - reset: function() { - this.dirty = false; + /** + * Utility method to focus an element, but only after a small amount of time. + */ + delay_focus: function($elem) { + setTimeout(function() { + $elem.focus(); + }, 50); }, + /** + * Utility method to get the widget options defined in the field xml description. + */ get_definition_options: function() { if (!this.definition_options) { var str = this.node.attrs.options || '{}'; this.definition_options = JSON.parse(str); } return this.definition_options; - } + }, + set_input_id: function(id) { + this.id_for_label = id; + }, }); -openerp.web.form.FieldChar = openerp.web.form.Field.extend({ - form_template: 'FieldChar', - init: function (view, node) { - this._super(view, node); - this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1'; - }, +/** + * A mixin to apply on any field that has to completely re-render when its readonly state + * switch. + */ +instance.web.form.ReinitializeFieldMixin = { + /** + * Default implementation of start(), use it or call explicitly initialize_field(). + */ start: function() { - this._super.apply(this, arguments); - this.$element.find('input').change(this.on_ui_change); + this._super(); + this.initialize_field(); }, - set_value: function(value) { - this._super.apply(this, arguments); - var show_value = openerp.web.format_value(value, this, ''); - this.$element.find('input').val(show_value); - return show_value; + initialize_field: function() { + this.on("change:effective_readonly", this, function() { + this.destroy_content(); + this.renderElement(); + this.initialize_content(); + this.render_value(); + }); + this.initialize_content(); + this.render_value(); }, - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('input').prop('readonly', this.readonly); + /** + * Called to destroy anything that could have been created previously, called before a + * re-initialization. + */ + destroy_content: function() {}, + /** + * Called to initialize the content. + */ + initialize_content: function() {}, + /** + * Called to render the value. Should also be explicitly called at the end of a set_value(). + */ + render_value: function() {}, +}; + +instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: 'FieldChar', + init: function (field_manager, node) { + this._super(field_manager, node); + this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1'; }, - set_value_from_ui: function() { - this.value = openerp.web.parse_value(this.$element.find('input').val(), this); - this._super(); + initialize_content: function() { + var self = this; + this.$element.find('input').change(function() { + self.set({'value': instance.web.parse_value(self.$element.find('input').val(), self)}); + }); }, - validate: function() { - this.invalid = false; - try { - var value = openerp.web.parse_value(this.$element.find('input').val(), this, ''); - this.invalid = this.required && value === ''; - } catch(e) { - this.invalid = true; + set_value: function(value_) { + this._super(value_); + this.render_value(); + }, + render_value: function() { + var show_value = instance.web.format_value(this.get('value'), this, ''); + if (!this.get("effective_readonly")) { + this.$element.find('input').val(show_value); + } else { + if (this.password) { + show_value = new Array(show_value.length + 1).join('*'); + } + this.$element.text(show_value); } }, - focus: function($element) { - this._super($element || this.$element.find('input:first')); + is_syntax_valid: function() { + if (!this.get("effective_readonly")) { + try { + var value_ = instance.web.parse_value(this.$element.find('input').val(), this, ''); + return true; + } catch(e) { + return false; + } + } + return true; + }, + is_false: function() { + return this.get('value') === ''; + }, + focus: function() { + this.delay_focus(this.$element.find('input:first')); } -}); +})); -openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({ - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('input').prop('readonly', true); - } +instance.web.form.FieldID = instance.web.form.FieldChar.extend({ + }); -openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({ - form_template: 'FieldEmail', - start: function() { - this._super.apply(this, arguments); +instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({ + template: 'FieldEmail', + initialize_content: function() { + this._super(); this.$element.find('button').click(this.on_button_clicked); }, + render_value: function() { + if (!this.get("effective_readonly")) { + this._super(); + } else { + this.$element.find('a') + .attr('href', 'mailto:' + this.get('value')) + .text(this.get('value')); + } + }, on_button_clicked: function() { - if (!this.value || !this.is_valid()) { + if (!this.get('value') || !this.is_syntax_valid()) { this.do_warn("E-mail error", "Can't send email to invalid e-mail address"); } else { - location.href = 'mailto:' + this.value; + location.href = 'mailto:' + this.get('value'); } } }); -openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({ - form_template: 'FieldUrl', - start: function() { - this._super.apply(this, arguments); +instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({ + template: 'FieldUrl', + initialize_content: function() { + this._super(); this.$element.find('button').click(this.on_button_clicked); }, + render_value: function() { + if (!this.get("effective_readonly")) { + this._super(); + } else { + var tmp = this.get('value'); + var s = /(\w+):(.+)/.exec(tmp); + if (!s) { + tmp = "http://" + this.get('value'); + } + this.$element.find('a').attr('href', tmp).text(tmp); + } + }, on_button_clicked: function() { - if (!this.value) { + if (!this.get('value')) { this.do_warn("Resource error", "This resource is empty"); } else { - window.open(this.value); + var url = $.trim(this.get('value')); + if(/^www\./i.test(url)) + url = 'http://'+url; + window.open(url); } } }); -openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({ - init: function (view, node) { - this._super(view, node); - if (node.attrs.digits) { +instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ + is_field_number: true, + init: function (field_manager, node) { + this._super(field_manager, node); + this.set({'value': 0}); + if (this.node.attrs.digits) { this.digits = py.eval(node.attrs.digits); } else { - this.digits = view.fields_view.fields[node.attrs.name].digits; + this.digits = this.field.digits; } }, - set_value: function(value) { - if (value === false || value === undefined) { + set_value: function(value_) { + if (value_ === false || value_ === undefined) { // As in GTK client, floats default to 0 - value = 0; + value_ = 0; } - this._super.apply(this, [value]); + this._super.apply(this, [value_]); } }); -openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({ +instance.web.DateTimeWidget = instance.web.OldWidget.extend({ template: "web.datetimepicker", jqueryui_object: 'datetimepicker', type_of_date: "datetime", @@ -1640,46 +1942,46 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({ showButtonPanel: true }); this.$element.find('img.oe_datepicker_trigger').click(function() { - if (!self.readonly && !self.picker('widget').is(':visible')) { - self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date()); + if (!self.get("effective_readonly") && !self.picker('widget').is(':visible')) { + self.picker('setDate', self.get('value') ? instance.web.auto_str_to_date(self.get('value')) : new Date()); self.$input_picker.show(); self.picker('show'); self.$input_picker.hide(); } }); this.set_readonly(false); - this.value = false; + this.set({'value': false}); }, picker: function() { return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments); }, - on_picker_select: function(text, instance) { + on_picker_select: function(text, instance_) { var date = this.picker('getDate'); this.$input.val(date ? this.format_client(date) : '').change(); }, - set_value: function(value) { - this.value = value; - this.$input.val(value ? this.format_client(value) : ''); + set_value: function(value_) { + this.set({'value': value_}); + this.$input.val(value_ ? this.format_client(value_) : ''); }, get_value: function() { - return this.value; + return this.get('value'); }, - set_value_from_ui: function() { - var value = this.$input.val() || false; - this.value = this.parse_client(value); + set_value_from_ui_: function() { + var value_ = this.$input.val() || false; + this.set({'value': this.parse_client(value_)}); }, set_readonly: function(readonly) { this.readonly = readonly; this.$input.prop('readonly', this.readonly); this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly); }, - is_valid: function(required) { - var value = this.$input.val(); - if (value === "") { - return !required; + is_valid_: function() { + var value_ = this.$input.val(); + if (value_ === "") { + return true; } else { try { - this.parse_client(value); + this.parse_client(value_); return true; } catch(e) { return false; @@ -1687,99 +1989,120 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({ } }, parse_client: function(v) { - return openerp.web.parse_value(v, {"widget": this.type_of_date}); + return instance.web.parse_value(v, {"widget": this.type_of_date}); }, format_client: function(v) { - return openerp.web.format_value(v, {"widget": this.type_of_date}); + return instance.web.format_value(v, {"widget": this.type_of_date}); }, on_change: function() { - if (this.is_valid()) { - this.set_value_from_ui(); + if (this.is_valid_()) { + this.set_value_from_ui_(); } } }); -openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({ +instance.web.DateWidget = instance.web.DateTimeWidget.extend({ jqueryui_object: 'datepicker', type_of_date: "date" }); -openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ - form_template: "EmptyComponent", +instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: "EmptyComponent", build_widget: function() { - return new openerp.web.DateTimeWidget(this); + return new instance.web.DateTimeWidget(this); }, - start: function() { - var self = this; - this._super.apply(this, arguments); - this.datewidget = this.build_widget(); - this.datewidget.on_change.add_last(this.on_ui_change); - this.datewidget.appendTo(this.$element); + destroy_content: function() { + if (this.datewidget) { + this.datewidget.destroy(); + this.datewidget = undefined; + } + }, + initialize_content: function() { + if (!this.get("effective_readonly")) { + this.datewidget = this.build_widget(); + this.datewidget.on_change.add_last(_.bind(function() { + this.set({'value': this.datewidget.get_value()}); + }, this)); + this.datewidget.appendTo(this.$element); + } }, - set_value: function(value) { - this._super(value); - this.datewidget.set_value(value); + set_value: function(value_) { + this._super(value_); + this.render_value(); }, - get_value: function() { - return this.datewidget.get_value(); + render_value: function() { + if (!this.get("effective_readonly")) { + this.datewidget.set_value(this.get('value')); + } else { + this.$element.text(instance.web.format_value(this.get('value'), this, '')); + } }, - update_dom: function() { - this._super.apply(this, arguments); - this.datewidget.set_readonly(this.readonly); + is_syntax_valid: function() { + if (!this.get("effective_readonly")) { + return this.datewidget.is_valid_(); + } + return true; }, - validate: function() { - this.invalid = !this.datewidget.is_valid(this.required); + is_false: function() { + return this.get('value') === ''; }, - focus: function($element) { - this._super($element || this.datewidget.$input); + focus: function() { + if (this.datewidget && this.datewidget.$input) + this.delay_focus(this.datewidget.$input); } -}); +})); -openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({ +instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({ build_widget: function() { - return new openerp.web.DateWidget(this); + return new instance.web.DateWidget(this); } }); -openerp.web.form.FieldText = openerp.web.form.Field.extend({ - form_template: 'FieldText', - start: function() { - this._super.apply(this, arguments); - this.$element.find('textarea').change(this.on_ui_change); +instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: 'FieldText', + initialize_content: function() { + this.$textarea = this.$element.find('textarea'); this.resized = false; + if (!this.get("effective_readonly")) { + this.$textarea.change(_.bind(function() { + this.set({'value': instance.web.parse_value(this.$textarea.val(), this)}); + }, this)); + } else { + this.$textarea.attr('disabled', 'disabled'); + } }, - set_value: function(value) { + set_value: function(value_) { this._super.apply(this, arguments); - var show_value = openerp.web.format_value(value, this, ''); - this.$element.find('textarea').val(show_value); + this.render_value(); + }, + render_value: function() { + var show_value = instance.web.format_value(this.get('value'), this, ''); + this.$textarea.val(show_value); if (!this.resized && this.view.options.resize_textareas) { this.do_resize(this.view.options.resize_textareas); this.resized = true; } }, - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('textarea').prop('readonly', this.readonly); - }, - set_value_from_ui: function() { - this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this); - this._super(); - }, - validate: function() { - this.invalid = false; - try { - var value = openerp.web.parse_value(this.$element.find('textarea').val(), this, ''); - this.invalid = this.required && value === ''; - } catch(e) { - this.invalid = true; + is_syntax_valid: function() { + if (!this.get("effective_readonly")) { + try { + var value_ = instance.web.parse_value(this.$textarea.val(), this, ''); + return true; + } catch(e) { + return false; + } } + return true; + }, + is_false: function() { + return this.get('value') === ''; }, focus: function($element) { - this._super($element || this.$element.find('textarea:first')); + this.delay_focus(this.$textarea); }, do_resize: function(max_height) { max_height = parseInt(max_height, 10); - var $input = this.$element.find('textarea'), + var $input = this.$textarea, $div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()), new_height; $div.text($input.val()); @@ -1797,64 +2120,60 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({ $div.remove(); $input.height(new_height); }, - reset: function() { - this.resized = false; - } -}); +})); -openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({ - form_template: 'FieldBoolean', +instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({ + template: 'FieldBoolean', start: function() { - var self = this; this._super.apply(this, arguments); - this.$element.find('input').click(self.on_ui_change); - }, - set_value: function(value) { - this._super.apply(this, arguments); - this.$element.find('input')[0].checked = value; - }, - set_value_from_ui: function() { - this.value = this.$element.find('input').is(':checked'); - this._super(); + this.$checkbox = $("input", this.$element); + this.$element.click(_.bind(function() { + this.set({'value': this.$checkbox.is(':checked')}); + }, this)); + var check_readonly = function() { + this.$checkbox.prop('disabled', this.get("effective_readonly")); + }; + this.on("change:effective_readonly", this, check_readonly); + _.bind(check_readonly, this)(); }, - update_dom: function() { + set_value: function(value_) { this._super.apply(this, arguments); - this.$element.find('input').prop('disabled', this.readonly); + this.$checkbox[0].checked = value_; }, - focus: function($element) { - this._super($element || this.$element.find('input:first')); + focus: function() { + this.delay_focus(this.$checkbox); } }); -openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({ - form_template: 'FieldProgressBar', +instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({ + template: 'FieldProgressBar', start: function() { this._super.apply(this, arguments); - this.$element.find('div').progressbar({ - value: this.value, - disabled: this.readonly + this.$element.progressbar({ + value: this.get('value'), + disabled: this.get("effective_readonly") }); }, - set_value: function(value) { + set_value: function(value_) { this._super.apply(this, arguments); - var show_value = Number(value); + var show_value = Number(value_); if (isNaN(show_value)) { show_value = 0; } - var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0'); - this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%'); + var formatted_value = instance.web.format_value(show_value, { type : 'float' }, '0'); + this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%'); } }); -openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({ +instance.web.form.FieldTextXml = instance.web.form.AbstractField.extend({ // to replace view editor }); -openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ - form_template: 'FieldSelection', - init: function(view, node) { +instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: 'FieldSelection', + init: function(field_manager, node) { var self = this; - this._super(view, node); + this._super(field_manager, node); this.values = _.clone(this.field.selection); _.each(this.values, function(v, i) { if (v[0] === false && v[1] === '') { @@ -1863,7 +2182,7 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ }); this.values.unshift([false, '']); }, - start: function() { + initialize_content: function() { // Flag indicating whether we're in an event chain containing a change // event on the select, in order to know what to do on keyup[RETURN]: // * If the user presses [RETURN] as part of changing the value of a @@ -1876,9 +2195,10 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ // changing the selected value), takes the action as validating the // row var ischanging = false; - this._super.apply(this, arguments); this.$element.find('select') - .change(this.on_ui_change) + .change(_.bind(function() { + this.set({'value': this.values[this.$element.find('select')[0].selectedIndex][0]}); + }, this)) .change(function () { ischanging = true; }) .click(function () { ischanging = false; }) .keyup(function (e) { @@ -1887,32 +2207,37 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ ischanging = false; }); }, - set_value: function(value) { - value = value === null ? false : value; - value = value instanceof Array ? value[0] : value; - this._super(value); - var index = 0; - for (var i = 0, ii = this.values.length; i < ii; i++) { - if (this.values[i][0] === value) index = i; - } - this.$element.find('select')[0].selectedIndex = index; - }, - set_value_from_ui: function() { - this.value = this.values[this.$element.find('select')[0].selectedIndex][0]; - this._super(); + set_value: function(value_) { + value_ = value_ === null ? false : value_; + value_ = value_ instanceof Array ? value_[0] : value_; + this._super(value_); + this.render_value(); }, - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('select').prop('disabled', this.readonly); + render_value: function() { + if (!this.get("effective_readonly")) { + var index = 0; + for (var i = 0, ii = this.values.length; i < ii; i++) { + if (this.values[i][0] === this.get('value')) index = i; + } + this.$element.find('select')[0].selectedIndex = index; + } else { + var self = this; + var option = _(this.values) + .detect(function (record) { return record[0] === self.get('value'); }); + this.$element.text(option ? option[1] : this.values[0][1]); + } }, - validate: function() { - var value = this.values[this.$element.find('select')[0].selectedIndex]; - this.invalid = !(value && !(this.required && value[0] === false)); + is_syntax_valid: function() { + if (this.get("effective_readonly")) { + return true; + } + var value_ = this.values[this.$element.find('select')[0].selectedIndex]; + return !! value_; }, - focus: function($element) { - this._super($element || this.$element.find('select:first')); + focus: function() { + this.delay_focus(this.$element.find('select:first')); } -}); +})); // jquery autocomplete tweak to allow html (function() { @@ -1921,8 +2246,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ function filter( array, term ) { var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); - return $.grep( array, function(value) { - return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() ); + return $.grep( array, function(value_) { + return matcher.test( $( "<div>" ).html( value_.label || value_.value || value_ ).text() ); }); } @@ -1946,114 +2271,88 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ }); })(); -openerp.web.form.dialog = function(content, options) { +instance.web.form.dialog = function(content, options) { options = _.extend({ width: '90%', height: 'auto', min_width: '800px' }, options || {}); - var dialog = new openerp.web.Dialog(null, options, content).open(); + var dialog = new instance.web.Dialog(null, options, content).open(); return dialog.$element; }; -openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ - form_template: 'FieldMany2One', - init: function(view, node) { - this._super(view, node); +instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: "FieldMany2One", + init: function(field_manager, node) { + this._super(field_manager, node); this.limit = 7; - this.value = null; - this.cm_id = _.uniqueId('m2o_cm_'); + this.set({'value': false}); + this.display_value = {}; this.last_search = []; - this.tmp_value = undefined; + this.floating = false; + this.inhibit_on_change = false; this.orderer = new openerp.web.DropMisordered(); }, start: function() { this._super(); + instance.web.form.ReinitializeFieldMixin.start.call(this); + this.on("change:value", this, function() { + this.floating = false; + this.render_value(); + }); + }, + initialize_content: function() { + if (!this.get("effective_readonly")) + this.render_editable(); + this.render_value(); + }, + render_editable: function() { var self = this; this.$input = this.$element.find("input"); + + self.$input.tipsy({ + title: function() { + return "No element was selected, you should create or select one from the dropdown list."; + }, + trigger:'manual', + fade: true, + }); + this.$drop_down = this.$element.find(".oe-m2o-drop-down-button"); - this.$menu_btn = this.$element.find(".oe-m2o-cm-button"); - - // context menu - var init_context_menu_def = $.Deferred().then(function(e) { - var rdataset = new openerp.web.DataSet(self, "ir.values", self.build_context()); - rdataset.call("get", ['action', 'client_action_relate', - [[self.field.relation, false]], false, rdataset.get_context()], false, 0) - .then(function(result) { - self.related_entries = result; - - var $cmenu = $("#" + self.cm_id); - $cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self})); - var bindings = {}; - bindings[self.cm_id + "_search"] = function() { - if (self.readonly) - return; - self._search_create_popup("search"); - }; - bindings[self.cm_id + "_create"] = function() { - if (self.readonly) - return; - self._search_create_popup("form"); - }; - bindings[self.cm_id + "_open"] = function() { - if (!self.value) { - return; - } - var pop = new openerp.web.form.FormOpenPopup(self.view); - pop.show_element( - self.field.relation, - self.value[0], - self.build_context(), - { - title: _t("Open: ") + (self.string || self.name) - } - ); - pop.on_write_completed.add_last(function() { - self.set_value(self.value[0]); - }); - }; - _.each(_.range(self.related_entries.length), function(i) { - bindings[self.cm_id + "_related_" + i] = function() { - self.open_related(self.related_entries[i]); - }; - }); - var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true, - bindings: bindings, itemStyle: {"color": ""}, - onContextMenu: function() { - if(self.value) { - $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").removeClass("oe-m2o-disabled-cm"); - } else { - $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm"); - } - if (!self.readonly) { - $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").removeClass("oe-m2o-disabled-cm"); - } else { - $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").addClass("oe-m2o-disabled-cm"); - } - return true; - }, menuStyle: {width: "200px"} - }); - $.async_when().then(function() {self.$menu_btn.trigger(e);}); + this.$follow_button = $(".oe-m2o-cm-button", this.$element); + + this.$follow_button.click(function() { + if (!self.get('value')) { + return; + } + var pop = new instance.web.form.FormOpenPopup(self.view); + pop.show_element( + self.field.relation, + self.get("value"), + self.build_context(), + { + title: _t("Open: ") + (self.string || self.name) + } + ); + pop.on_write_completed.add_last(function() { + self.display_value = {}; + self.render_value(); }); }); - var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()}; - this.$menu_btn.click(ctx_callback); // some behavior for input this.$input.keyup(function() { if (self.$input.val() === "") { - self._change_int_value(null); - } else if (self.value === null || (self.value && self.$input.val() !== self.value[1])) { - self._change_int_value(undefined); + self.set({value: false}); + } else { + self.floating = true; } }); this.$drop_down.click(function() { - if (self.readonly) - return; if (self.$input.autocomplete("widget").is(":visible")) { self.$input.autocomplete("close"); } else { - if (self.value) { + if (self.get("value") && ! self.floating) { self.$input.autocomplete("search", ""); } else { self.$input.autocomplete("search"); @@ -2061,16 +2360,43 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ self.$input.focus(); } }); + var tip_def = $.Deferred(); + var untip_def = $.Deferred(); + var tip_delay = 200; + var tip_duration = 3000; var anyoneLoosesFocus = function() { - if (!self.$input.is(":focus") && - !self.$input.autocomplete("widget").is(":visible") && - !self.value) { - if (self.value === undefined && self.last_search.length > 0) { - self._change_int_ext_value(self.last_search[0]); + if (self.floating) { + if (self.last_search.length > 0) { + if (self.last_search[0][0] != self.get("value")) { + self.display_value = {}; + self.display_value["" + self.last_search[0][0]] = self.last_search[0][1]; + self.set({value: self.last_search[0][0]}); + } else { + self.render_value(); + } } else { - self._change_int_ext_value(null); + self.set({value: false}); } } + if (! self.get("value")) { + tip_def.reject(); + untip_def.reject(); + tip_def = $.Deferred(); + tip_def.then(function() { + self.$input.tipsy("show"); + }); + setTimeout(function() { + tip_def.resolve(); + untip_def.reject(); + untip_def = $.Deferred(); + untip_def.then(function() { + self.$input.tipsy("hide"); + }); + setTimeout(function() {untip_def.resolve();}, tip_duration); + }, tip_delay); + } else { + tip_def.reject(); + } }; this.$input.focusout(anyoneLoosesFocus); @@ -2082,9 +2408,11 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ isSelecting = true; var item = ui.item; if (item.id) { - self._change_int_value([item.id, item.name]); + self.display_value = {}; + self.display_value["" + item.id] = item.name; + self.set({value: item.id}); } else if (item.action) { - self._change_int_value(undefined); + self.floating = true; item.action(); return false; } @@ -2112,7 +2440,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ var search_val = request.term; var self = this; - var dataset = new openerp.web.DataSet(this, this.field.relation, self.build_context()); + var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context()); this.orderer.add(dataset.name_search( search_val, self.build_domain(), 'ilike', this.limit + 1)).then(function(data) { @@ -2133,7 +2461,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ values.push({label: _t("<em> Search More...</em>"), action: function() { dataset.name_search(search_val, self.build_domain(), 'ilike' , false, function(data) { - self._change_int_value(null); self._search_create_popup("search", data); }); }}); @@ -2142,7 +2469,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ var raw_result = _(data.result).map(function(x) {return x[1];}); if (search_val.length > 0 && !_.include(raw_result, search_val) && - (!self.value || search_val !== self.value[1])) { + (!self.get("value") || self.floating)) { values.push({label: _.str.sprintf(_t('<em> Create "<strong>%s</strong>"</em>'), $('<span />').text(search_val).html()), action: function() { self._quick_create(search_val); @@ -2150,7 +2477,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ } // create... values.push({label: _t("<em> Create and Edit...</em>"), action: function() { - self._change_int_value(null); self._search_create_popup("form", undefined, {"default_name": search_val}); }}); @@ -2160,13 +2486,14 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ _quick_create: function(name) { var self = this; var slow_create = function () { - self._change_int_value(null); self._search_create_popup("form", undefined, {"default_name": name}); }; if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) { - new openerp.web.DataSet(this, this.field.relation, self.build_context()) + new instance.web.DataSet(this, this.field.relation, self.build_context()) .name_create(name, function(data) { - self._change_int_ext_value(data); + self.display_value = {}; + self.display_value["" + data[0]] = data[1]; + self.set({value: data[0]}); }).fail(function(error, event) { event.preventDefault(); slow_create(); @@ -2177,7 +2504,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ // all search/create popup handling _search_create_popup: function(view, ids, context) { var self = this; - var pop = new openerp.web.form.SelectCreatePopup(this); + var pop = new instance.web.form.SelectCreatePopup(this); pop.select_element( self.field.relation, { @@ -2187,100 +2514,71 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ disable_multiple_selection: true }, self.build_domain(), - new openerp.web.CompoundContext(self.build_context(), context || {}) + new instance.web.CompoundContext(self.build_context(), context || {}) ); pop.on_select_elements.add(function(element_ids) { - new openerp.web.DataSet(self, self.field.relation, self.build_context()) - .name_get([element_ids[0]], function(data) { - self._change_int_ext_value(data[0]); - }); + self.set({value: element_ids[0]}); }); }, - _change_int_ext_value: function(value) { - this._change_int_value(value); - this.$input.val(this.value ? this.value[1] : ""); - }, - _change_int_value: function(value) { - this.value = value; - var back_orig_value = this.original_value; - if (this.value === null || this.value) { - this.original_value = this.value; + render_value: function(no_recurse) { + var self = this; + if (! this.get("value")) { + this.display_string(""); + return; } - if (back_orig_value === undefined) { // first use after a set_value() + var display = this.display_value["" + this.get("value")]; + if (display) { + this.display_string(display); return; } - if (this.value !== undefined && ((back_orig_value ? back_orig_value[0] : null) - !== (this.value ? this.value[0] : null))) { - this.on_ui_change(); + if (! no_recurse) { + var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context()); + dataset.name_get([self.get("value")], function(data) { + self.display_value["" + self.get("value")] = data[0][1]; + self.render_value(true); + }); } }, - set_value: function(value) { - value = value || null; - this.invalid = false; + display_string: function(str) { var self = this; - this.tmp_value = value; - self.update_dom(); - self.on_value_changed(); - var real_set_value = function(rval) { - self.tmp_value = undefined; - self.value = rval; - self.original_value = undefined; - self._change_int_ext_value(rval); - }; - if (value && !(value instanceof Array)) { - // name_get in a m2o does not use the context of the field - new openerp.web.DataSet(this, this.field.relation, self.view.dataset.get_context()) - .name_get([value], function(data) { - real_set_value(data[0]); - }).fail(function() {self.tmp_value = undefined;}); + if (!this.get("effective_readonly")) { + this.$input.val(str); } else { - $.async_when().then(function() {real_set_value(value);}); - } - }, - get_value: function() { - if (this.tmp_value !== undefined) { - if (this.tmp_value instanceof Array) { - return this.tmp_value[0]; - } - return this.tmp_value ? this.tmp_value : false; - } - if (this.value === undefined) - return this.original_value ? this.original_value[0] : false; - return this.value ? this.value[0] : false; - }, - validate: function() { - this.invalid = false; - var val = this.tmp_value !== undefined ? this.tmp_value : this.value; - if (val === null) { - this.invalid = this.required; + this.$element.find('a') + .unbind('click') + .text(str) + .click(function () { + self.do_action({ + type: 'ir.actions.act_window', + res_model: self.field.relation, + res_id: self.get("value"), + context: self.build_context(), + views: [[false, 'form']], + target: 'current' + }); + return false; + }); } }, - open_related: function(related) { + set_value: function(value_) { var self = this; - if (!self.value) - return; - var additional_context = { - active_id: self.value[0], - active_ids: [self.value[0]], - active_model: self.field.relation - }; - self.rpc("/web/action/load", { - action_id: related[2].id, - context: additional_context - }, function(result) { - result.result.context = _.extend(result.result.context || {}, additional_context); - self.do_action(result.result); - }); + if (value_ instanceof Array) { + this.display_value = {}; + this.display_value["" + value_[0]] = value_[1]; + value_ = value_[0]; + } + value_ = value_ || false; + this.inhibit_on_change = true; + this._super(value_); + this.inhibit_on_change = false; }, - focus: function ($element) { - this._super($element || this.$input); + is_false: function() { + return ! this.get("value"); }, - update_dom: function() { - this._super.apply(this, arguments); - this.$input.prop('readonly', this.readonly); + focus: function () { + this.delay_focus(this.$input); } -}); -openerp.web.check_interface(openerp.web.form.FieldMany2One, openerp.web.form.FieldInterface); +})); /* # Values: (0, 0, { fields }) create @@ -2327,24 +2625,24 @@ var commands = { return [6, false, ids]; } }; -openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ - form_template: 'FieldOne2Many', +instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ multi_selection: false, - init: function(view, node) { - this._super(view, node); + disable_utility_classes: true, + init: function(field_manager, node) { + this._super(field_manager, node); + lazy_build_o2m_kanban_view(); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; this.is_setted = $.Deferred(); this.form_last_update = $.Deferred(); this.init_form_last_update = this.form_last_update; - this.disable_utility_classes = true; }, start: function() { this._super.apply(this, arguments); var self = this; - this.dataset = new openerp.web.form.One2ManyDataSet(this, this.field.relation); + this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation); this.dataset.o2m = this; this.dataset.parent_view = this.view; this.dataset.child_name = this.name; @@ -2356,16 +2654,23 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ this.is_setted.then(function() { self.load_views(); }); + this.is_loaded.then(function() { + self.on("change:effective_readonly", self, function() { + self.is_loaded = self.is_loaded.pipe(function() { + self.viewmanager.destroy(); + return $.when(self.load_views()).then(function() { + self.reload_current_view(); + }); + }); + }); + }); }, trigger_on_change: function() { var tmp = this.doing_on_change; this.doing_on_change = true; - this.on_ui_change(); + this.trigger('changed_value'); this.doing_on_change = tmp; }, - is_readonly: function() { - return this.readonly || this.force_readonly; - }, load_views: function() { var self = this; @@ -2376,21 +2681,21 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ var view = { view_id: false, view_type: mode == "tree" ? "list" : mode, - options: { sidebar : false } + options: {} }; if (self.field.views && self.field.views[mode]) { view.embedded_view = self.field.views[mode]; } if(view.view_type === "list") { view.options.selectable = self.multi_selection; - if (self.is_readonly()) { + if (self.get("effective_readonly")) { view.options.addable = null; view.options.deletable = null; view.options.isClarkGable = false; } } else if (view.view_type === "form") { - if (self.is_readonly()) { - view.view_type = 'page'; + if (self.get("effective_readonly")) { + view.view_type = 'form'; } view.options.not_interactible_on_create = true; } @@ -2398,12 +2703,14 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ }); this.views = views; - this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {}); + this.viewmanager = new instance.web.ViewManager(this, this.dataset, views, { + $sidebar: false, + }); this.viewmanager.template = 'One2Many.viewmanager'; - this.viewmanager.registry = openerp.web.views.extend({ - list: 'openerp.web.form.One2ManyListView', - form: 'openerp.web.form.One2ManyFormView', - page: 'openerp.web.PageView' + this.viewmanager.registry = instance.web.views.extend({ + list: 'instance.web.form.One2ManyListView', + form: 'instance.web.form.One2ManyFormView', + kanban: 'instance.web.form.One2ManyKanbanView', }); var once = $.Deferred().then(function() { self.init_form_last_update.resolve(); @@ -2412,12 +2719,12 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ self.initial_is_loaded.resolve(); }); this.viewmanager.on_controller_inited.add_last(function(view_type, controller) { + controller.o2m = self; if (view_type == "list") { - controller.o2m = self; - if (self.is_readonly()) + if (self.get("effective_readonly")) controller.set_editable(false); - } else if (view_type == "form" || view_type == 'page') { - if (view_type == 'page' || self.is_readonly()) { + } else if (view_type === "form") { + if (self.get("effective_readonly")) { $(".oe_form_buttons", controller.$element).children().remove(); } controller.on_record_loaded.add_last(function() { @@ -2451,7 +2758,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ var view = self.viewmanager.views[active_view].controller; if(active_view === "list") { return view.reload_content(); - } else if (active_view === "form" || active_view === 'page') { + } else if (active_view === "form") { if (self.dataset.index === null && self.dataset.ids.length >= 1) { self.dataset.index = 0; } @@ -2460,18 +2767,18 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ }; self.form_last_update = self.form_last_update.pipe(act, act); return self.form_last_update; - } else if (active_view === "graph") { + } else if (view.do_search) { return view.do_search(self.build_domain(), self.dataset.get_context(), []); } }, undefined); }, - set_value: function(value) { - value = value || []; + set_value: function(value_) { + value_ = value_ || []; var self = this; this.dataset.reset_ids([]); - if(value.length >= 1 && value[0] instanceof Array) { + if(value_.length >= 1 && value_[0] instanceof Array) { var ids = []; - _.each(value, function(command) { + _.each(value_, function(command) { var obj = {values: command[2]}; switch (command[0]) { case commands.CREATE: @@ -2500,10 +2807,10 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ }); this._super(ids); this.dataset.set_ids(ids); - } else if (value.length >= 1 && typeof(value[0]) === "object") { + } else if (value_.length >= 1 && typeof(value_[0]) === "object") { var ids = []; this.dataset.delete_all = true; - _.each(value, function(command) { + _.each(value_, function(command) { var obj = {values: command}; obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix); obj.defaults = {}; @@ -2514,8 +2821,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ this._super(ids); this.dataset.set_ids(ids); } else { - this._super(value); - this.dataset.reset_ids(value); + this._super(value_); + this.dataset.reset_ids(value_); } if (this.dataset.index === null && this.dataset.ids.length > 0) { this.dataset.index = 0; @@ -2572,61 +2879,37 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ return false; }, this)); }, - is_valid: function() { - this.validate(); - return this._super(); - }, - validate: function() { - this.invalid = false; + is_syntax_valid: function() { if (!this.viewmanager.views[this.viewmanager.active_view]) - return; + return true; var view = this.viewmanager.views[this.viewmanager.active_view].controller; if (this.viewmanager.active_view === "form") { for (var f in view.fields) { f = view.fields[f]; if (!f.is_valid()) { - this.invalid = true; - return; + return false; } } } + return true; }, - is_dirty: function() { - this.save_any_view(); - return this._super(); - }, - update_dom: function() { - this._super.apply(this, arguments); - var self = this; - if (this.previous_readonly !== this.readonly) { - this.previous_readonly = this.readonly; - if (this.viewmanager) { - this.is_loaded = this.is_loaded.pipe(function() { - self.viewmanager.destroy(); - return $.when(self.load_views()).then(function() { - self.reload_current_view(); - }); - }); - } - } - } }); -openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({ +instance.web.form.One2ManyDataSet = instance.web.BufferedDataSet.extend({ get_context: function() { this.context = this.o2m.build_context([this.o2m.name]); return this.context; } }); -openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ +instance.web.form.One2ManyListView = instance.web.ListView.extend({ _template: 'One2Many.listview', do_add_record: function () { if (this.options.editable) { this._super.apply(this, arguments); } else { var self = this; - var pop = new openerp.web.form.SelectCreatePopup(this); + var pop = new instance.web.form.SelectCreatePopup(this); pop.on_default_get.add(self.dataset.on_default_get); pop.select_element( self.o2m.field.relation, @@ -2657,7 +2940,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ }, do_activate_record: function(index, id) { var self = this; - var pop = new openerp.web.form.FormOpenPopup(self.o2m.view); + var pop = new instance.web.form.FormOpenPopup(self.o2m.view); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.name, auto_write: false, @@ -2668,7 +2951,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); }, form_view_options: {'not_interactible_on_create':true}, - readonly: self.o2m.is_readonly() + readonly: self.o2m.get("effective_readonly") }); pop.on_write.add(function(id, data) { self.o2m.dataset.write(id, data, {}, function(r) { @@ -2683,12 +2966,12 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ } }); -openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({ +instance.web.form.One2ManyFormView = instance.web.FormView.extend({ form_template: 'One2Many.formview', on_loaded: function(data) { this._super(data); var self = this; - this.$form_header.find('button.oe_form_button_create').click(function() { + this.$buttons.find('button.oe_form_button_create').click(function() { self.do_save().then(self.on_button_new); }); }, @@ -2701,12 +2984,43 @@ openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({ } }); -openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({ - form_template: 'FieldMany2Many', +var lazy_build_o2m_kanban_view = function() { +if (! instance.web_kanban || instance.web.form.One2ManyKanbanView) + return; +instance.web.form.One2ManyKanbanView = instance.web_kanban.KanbanView.extend({ + open_record: function(id) { + var self = this; + var pop = new instance.web.form.FormOpenPopup(self.o2m.view); + pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { + title: _t("Open: ") + self.name, + auto_write: false, + alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, + parent_view: self.o2m.view, + child_name: self.o2m.name, + read_function: function() { + return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); + }, + form_view_options: {'not_interactible_on_create':true}, + readonly: self.o2m.get("effective_readonly"), + }); + pop.on_write.add(function(id, data) { + self.o2m.dataset.write(id, data, {}, function(r) { + self.o2m.reload_current_view(); + }); + }); + + }, +}); +} + +/* + * TODO niv: clean those deferred stuff, it could be better + */ +instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ multi_selection: false, - init: function(view, node) { - this._super(view, node); - this.list_id = _.uniqueId("many2many"); + disable_utility_classes: true, + init: function(field_manager, node) { + this._super(field_manager, node); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; this.is_setted = $.Deferred(); @@ -2716,43 +3030,44 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({ var self = this; - this.dataset = new openerp.web.form.Many2ManyDataSet(this, this.field.relation); + this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); this.dataset.m2m = this; this.dataset.on_unlink.add_last(function(ids) { - self.on_ui_change(); + self.dataset_changed(); }); this.is_setted.then(function() { self.load_view(); }); + this.is_loaded.then(function() { + self.on("change:effective_readonly", self, function() { + self.is_loaded = self.is_loaded.pipe(function() { + self.list_view.destroy(); + return $.when(self.load_view()).then(function() { + self.reload_content(); + }); + }); + }); + }) }, - set_value: function(value) { - value = value || []; - if (value.length >= 1 && value[0] instanceof Array) { - value = value[0][2]; + set_value: function(value_) { + value_ = value_ || []; + if (value_.length >= 1 && value_[0] instanceof Array) { + value_ = value_[0][2]; } - this._super(value); - this.dataset.set_ids(value); + this._super(value_); + this.dataset.set_ids(value_); var self = this; self.reload_content(); this.is_setted.resolve(); }, - get_value: function() { - return [commands.replace_with(this.dataset.ids)]; - }, - validate: function() { - this.invalid = false; - }, - is_readonly: function() { - return this.readonly || this.force_readonly; - }, load_view: function() { var self = this; - this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, { - 'addable': self.is_readonly() ? null : _t("Add"), - 'deletable': self.is_readonly() ? false : true, + this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, { + 'addable': self.get("effective_readonly") ? null : _t("Add"), + 'deletable': self.get("effective_readonly") ? false : true, 'selectable': self.multi_selection, - 'isClarkGable': self.is_readonly() ? false : true + 'isClarkGable': self.get("effective_readonly") ? false : true }); var embedded = (this.field.views || {}).tree; if (embedded) { @@ -2765,7 +3080,7 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({ loaded.resolve(); }); $.async_when().then(function () { - self.list_view.appendTo($("#" + self.list_id)); + self.list_view.appendTo(self.$element); }); return loaded; }, @@ -2775,24 +3090,12 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({ return self.list_view.reload_content(); }); }, - update_dom: function() { - this._super.apply(this, arguments); - var self = this; - if (this.previous_readonly !== this.readonly) { - this.previous_readonly = this.readonly; - if (this.list_view) { - this.is_loaded = this.is_loaded.pipe(function() { - self.list_view.destroy(); - return $.when(self.load_view()).then(function() { - self.reload_content(); - }); - }); - } - } - } + dataset_changed: function() { + this.set({'value': [commands.replace_with(this.dataset.ids)]}); + }, }); -openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({ +instance.web.form.Many2ManyDataSet = instance.web.DataSetStatic.extend({ get_context: function() { this.context = this.m2m.build_context(); return this.context; @@ -2801,25 +3104,25 @@ openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({ /** * @class - * @extends openerp.web.ListView + * @extends instance.web.ListView */ -openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{ +instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends instance.web.form.Many2ManyListView# */{ do_add_record: function () { - var pop = new openerp.web.form.SelectCreatePopup(this); + var pop = new instance.web.form.SelectCreatePopup(this); pop.select_element( this.model, { title: _t("Add: ") + this.name }, - new openerp.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]), + new instance.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]), this.m2m_field.build_context() ); var self = this; pop.on_select_elements.add(function(element_ids) { - _.each(element_ids, function(element_id) { - if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) { - self.dataset.set_ids([].concat(self.dataset.ids, [element_id])); - self.m2m_field.on_ui_change(); + _.each(element_ids, function(one_id) { + if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) { + self.dataset.set_ids([].concat(self.dataset.ids, [one_id])); + self.m2m_field.dataset_changed(); self.reload_content(); } }); @@ -2827,10 +3130,10 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open }, do_activate_record: function(index, id) { var self = this; - var pop = new openerp.web.form.FormOpenPopup(this); + var pop = new instance.web.form.FormOpenPopup(this); pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), { title: _t("Open: ") + this.name, - readonly: this.getParent().is_readonly() + readonly: this.getParent().get("effective_readonly") }); pop.on_write_completed.add_last(function() { self.reload_content(); @@ -2840,9 +3143,9 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open /** * @class - * @extends openerp.web.OldWidget + * @extends instance.web.OldWidget */ -openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.SelectCreatePopup# */{ +instance.web.form.SelectCreatePopup = instance.web.OldWidget.extend(/** @lends instance.web.form.SelectCreatePopup# */{ template: "SelectCreatePopup", /** * options: @@ -2868,7 +3171,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.initial_ids = this.options.initial_ids; this.created_elements = []; this.renderElement(); - openerp.web.form.dialog(this.$element, { + instance.web.form.dialog(this.$element, { close: function() { self.check_exit(); }, @@ -2879,7 +3182,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope start: function() { this._super(); var self = this; - this.dataset = new openerp.web.ProxyDataSet(this, this.model, + this.dataset = new instance.web.ProxyDataSet(this, this.model, this.context); this.dataset.create_function = function() { return self.options.create_function.apply(null, arguments).then(function(r) { @@ -2899,10 +3202,10 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope contexts: [this.context] }, function (results) { var search_defaults = {}; - _.each(results.context, function (value, key) { + _.each(results.context, function (value_, key) { var match = /^search_default_(.*)$/.exec(key); if (match) { - search_defaults[match[1]] = value; + search_defaults[match[1]] = value_; } }); self.setup_search_view(search_defaults); @@ -2920,7 +3223,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope if (this.searchview) { this.searchview.destroy(); } - this.searchview = new openerp.web.SearchView(this, + this.searchview = new instance.web.SearchView(this, this.dataset, false, search_defaults); this.searchview.on_search.add(function(domains, contexts, groupbys) { if (self.initial_ids) { @@ -2932,13 +3235,13 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope } }); this.searchview.on_loaded.add_last(function () { - self.view_list = new openerp.web.form.SelectCreateListView(self, + self.view_list = new instance.web.form.SelectCreateListView(self, self.dataset, false, _.extend({'deletable': false, 'selectable': !self.options.disable_multiple_selection }, self.options.list_view_options || {})); self.view_list.popup = self; - self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() { + self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() { self.view_list.do_show(); }).pipe(function() { self.searchview.do_search(); @@ -2960,7 +3263,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope }); }); }); - this.searchview.appendTo($("#" + this.element_id + "_search")); + this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element)); }, do_search: function(domains, contexts, groupbys) { var self = this; @@ -2974,14 +3277,14 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope }, create_row: function() { var self = this; - var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain); + var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain); wdataset.parent_view = this.options.parent_view; wdataset.child_name = this.options.child_name; return wdataset.create.apply(wdataset, arguments); }, write_row: function() { var self = this; - var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain); + var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain); wdataset.parent_view = this.options.parent_view; wdataset.child_name = this.options.child_name; return wdataset.write.apply(wdataset, arguments); @@ -3005,11 +3308,11 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.view_list.$element.hide(); } this.dataset.index = null; - this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options); + this.view_form = new instance.web.FormView(this, this.dataset, false, self.options.form_view_options); if (this.options.alternative_form_view) { this.view_form.set_embedded_view(this.options.alternative_form_view); } - this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form")); + this.view_form.appendTo(this.$element.find(".oe-select-create-popup-view-form")); this.view_form.on_loaded.add_last(function() { var $buttons = self.view_form.$element.find(".oe_form_buttons"); $buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self})); @@ -3045,7 +3348,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope on_default_get: function(res) {} }); -openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({ +instance.web.form.SelectCreateListView = instance.web.ListView.extend({ do_add_record: function () { this.popup.new_object(); }, @@ -3061,9 +3364,9 @@ openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({ /** * @class - * @extends openerp.web.OldWidget + * @extends instance.web.OldWidget */ -openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.FormOpenPopup# */{ +instance.web.form.FormOpenPopup = instance.web.OldWidget.extend(/** @lends instance.web.form.FormOpenPopup# */{ template: "FormOpenPopup", /** * options: @@ -3081,7 +3384,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp this.context = context || {}; this.options = _.defaults(options || {}, {"auto_write": true}); this.renderElement(); - openerp.web.dialog(this.$element, { + instance.web.dialog(this.$element, { title: options.title || '', modal: true, width: 960, @@ -3091,7 +3394,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp }, start: function() { this._super(); - this.dataset = new openerp.web.form.FormOpenDataset(this, this.model, this.context); + this.dataset = new instance.web.form.FormOpenDataset(this, this.model, this.context); this.dataset.fop = this; this.dataset.ids = [this.row_id]; this.dataset.index = 0; @@ -3103,7 +3406,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp if (!this.options.auto_write) return; var self = this; - var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain); + var wdataset = new instance.web.DataSetSearch(this, this.model, this.context, this.domain); wdataset.parent_view = this.options.parent_view; wdataset.child_name = this.options.child_name; wdataset.write(id, data, {}, function(r) { @@ -3113,14 +3416,14 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp on_write_completed: function() {}, setup_form_view: function() { var self = this; - var FormClass = this.options.readonly - ? openerp.web.views.get_object('page') - : openerp.web.views.get_object('form'); - this.view_form = new FormClass(this, this.dataset, false, self.options.form_view_options); + var FormClass = instance.web.views.get_object('form'); + var options = _.clone(self.options.form_view_options) || {}; + options.initial_mode = this.options.readonly ? "view" : "edit"; + this.view_form = new FormClass(this, this.dataset, false, options); if (this.options.alternative_form_view) { this.view_form.set_embedded_view(this.options.alternative_form_view); } - this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form")); + this.view_form.appendTo(this.$element.find(".oe-form-open-popup-form-view")); this.view_form.on_loaded.add_last(function() { var $buttons = self.view_form.$element.find(".oe_form_buttons"); $buttons.html(QWeb.render("FormOpenPopup.form.buttons")); @@ -3144,7 +3447,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp } }); -openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({ +instance.web.form.FormOpenDataset = instance.web.ProxyDataSet.extend({ read_ids: function() { if (this.fop.options.read_function) { return this.fop.options.read_function.apply(null, arguments); @@ -3154,40 +3457,28 @@ openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({ } }); -openerp.web.form.FieldReference = openerp.web.form.Field.extend({ - form_template: 'FieldReference', - init: function(view, node) { - this._super(view, node); +instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + template: 'FieldReference', + init: function(field_manager, node) { + this._super(field_manager, node); this.fields_view = { fields: { selection: { - selection: view.fields_view.fields[this.name].selection + selection: this.view.fields_view.fields[this.name].selection }, m2o: { relation: null } } }; - this.get_fields_values = view.get_fields_values; - this.get_selected_ids = view.get_selected_ids; + this.get_fields_values = this.view.get_fields_values; + this.get_selected_ids = this.view.get_selected_ids; this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop; this.dataset = this.view.dataset; - this.widgets_counter = 0; this.view_id = 'reference_' + _.uniqueId(); - this.widgets = {}; this.fields = {}; this.fields_order = []; - this.selection = new openerp.web.form.FieldSelection(this, { attrs: { - name: 'selection', - widget: 'selection' - }}); this.reference_ready = true; - this.selection.on_value_changed.add_last(this.on_selection_changed); - this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: { - name: 'm2o', - widget: 'many2one' - }}); - this.m2o.on_ui_change.add_last(this.on_ui_change); }, on_nop: function() { }, @@ -3195,59 +3486,82 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({ if (this.reference_ready) { var sel = this.selection.get_value(); this.m2o.field.relation = sel; - this.m2o.set_value(null); + this.m2o.set_value(false); this.m2o.$element.toggle(sel !== false); } }, - start: function() { - this._super(); + destroy_content: function() { + if (this.selection) { + this.selection.destroy(); + this.selection = undefined; + } + if (this.m2o) { + this.m2o.destroy(); + this.m2o = undefined; + } + }, + initialize_content: function() { + this.selection = new instance.web.form.FieldSelection(this, { attrs: { + name: 'selection', + widget: 'selection' + }}); + this.selection.set({readonly: this.get('effective_readonly')}); + this.selection.on("change:value", this, this.on_selection_changed); this.selection.$element = $(".oe_form_view_reference_selection", this.$element); this.selection.renderElement(); this.selection.start(); + + this.m2o = new instance.web.form.FieldMany2One(this, { attrs: { + name: 'm2o', + widget: 'many2one' + }}); + this.m2o.set({"readonly": this.get("effective_readonly")}); + this.m2o.on("change:value", this, this.data_changed); this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element); this.m2o.renderElement(); this.m2o.start(); }, - is_valid: function() { - return this.required === false || typeof(this.get_value()) === 'string'; + is_false: function() { + return typeof(this.get_value()) !== 'string'; }, - is_dirty: function() { - return this.selection.is_dirty() || this.m2o.is_dirty(); + set_value: function(value_) { + this._super(value_); + this.render_value(); }, - set_value: function(value) { - this._super(value); + render_value: function() { this.reference_ready = false; var vals = [], sel_val, m2o_val; - if (typeof(value) === 'string') { - vals = value.split(','); + if (typeof(this.get('value')) === 'string') { + vals = this.get('value').split(','); } sel_val = vals[0] || false; m2o_val = vals[1] ? parseInt(vals[1], 10) : false; - this.selection.set_value(sel_val); + if (!this.get("effective_readonly")) { + this.selection.set_value(sel_val); + } this.m2o.field.relation = sel_val; this.m2o.set_value(m2o_val); this.m2o.$element.toggle(sel_val !== false); this.reference_ready = true; }, - get_value: function() { + data_changed: function() { var model = this.selection.get_value(), id = this.m2o.get_value(); if (typeof(model) === 'string' && typeof(id) === 'number') { - return model + ',' + id; + this.set({'value': model + ',' + id}); } else { - return false; + this.set({'value': false}); } - } -}); + }, +})); -openerp.web.form.FieldBinary = openerp.web.form.Field.extend({ - init: function(view, node) { - this._super(view, node); +instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, { + init: function(field_manager, node) { + this._super(field_manager, node); this.iframe = this.element_id + '_iframe'; this.binary_value = false; }, - start: function() { - this._super.apply(this, arguments); + initialize_content: function() { this.$element.find('input.oe-binary-file').change(this.on_file_change); this.$element.find('button.oe-binary-file-save').click(this.on_save_as); this.$element.find('.oe-binary-file-clear').click(this.on_clear); @@ -3281,7 +3595,6 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({ console.warn("Error while uploading file : ", name); } else { this.on_file_uploaded_and_valid.apply(this, arguments); - this.on_ui_change(); } this.$element.find('.oe-binary-progress').hide(); this.$element.find('.oe-binary').show(); @@ -3300,48 +3613,64 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({ context: this.view.dataset.get_context() })}, complete: $.unblockUI, - error: openerp.webclient.crashmanager.on_rpc_error + error: instance.webclient.crashmanager.on_rpc_error }); }, on_clear: function() { - if (this.value !== false) { - this.value = false; + if (this.get('value') !== false) { this.binary_value = false; - this.on_ui_change(); + this.set({'value': false}); } return false; } -}); +})); -openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({ - form_template: 'FieldBinaryFile', - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly); - this.$element.find('input[type=text]').prop('readonly', this.readonly); +instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ + template: 'FieldBinaryFile', + initialize_content: function() { + this._super(); + if (this.get("effective_readonly")) { + var self = this; + this.$element.find('a').click(function() { + if (self.get('value')) { + self.on_save_as(); + } + return false; + }); + } }, - set_value: function(value) { + set_value: function(value_) { this._super.apply(this, arguments); - var show_value; - if (this.node.attrs.filename) { - show_value = this.view.datarecord[this.node.attrs.filename] || ''; + this.render_value(); + }, + render_value: function() { + if (!this.get("effective_readonly")) { + var show_value; + if (this.node.attrs.filename) { + show_value = this.view.datarecord[this.node.attrs.filename] || ''; + } else { + show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : ''; + } + this.$element.find('input').eq(0).val(show_value); } else { - show_value = (value != null && value !== false) ? value : ''; + this.$element.find('a').show(!!this.get('value')); + if (this.get('value')) { + var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || ''); + this.$element.find('a').text(show_value); + } } - this.$element.find('input').eq(0).val(show_value); }, on_file_uploaded_and_valid: function(size, name, content_type, file_base64) { - this.value = file_base64; this.binary_value = true; + this.set({'value': file_base64}); var show_value = name + " (" + this.human_filesize(size) + ")"; this.$element.find('input').eq(0).val(show_value); this.set_filename(name); }, - set_filename: function(value) { + set_filename: function(value_) { var filename = this.node.attrs.filename; if (this.view.fields[filename]) { - this.view.fields[filename].set_value(value); - this.view.fields[filename].on_ui_change(); + this.view.fields[filename].set({value: value_}); } }, on_clear: function() { @@ -3351,58 +3680,59 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({ } }); -openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({ - form_template: 'FieldBinaryImage', - start: function() { - this._super.apply(this, arguments); - this.$image = this.$element.find('img.oe-binary-image'); +instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ + template: 'FieldBinaryImage', + initialize_content: function() { + this._super(); + this.$placeholder = $(".oe_form_field-binary-image-placeholder", this.$element); + if (!this.get("effective_readonly")) + this.$element.find('.oe-binary').show(); + else + this.$element.find('.oe-binary').hide(); }, - update_dom: function() { + set_value: function(value_) { this._super.apply(this, arguments); - this.$element.find('.oe-binary').toggle(!this.readonly); + this.render_value(); }, - set_value: function(value) { - this._super.apply(this, arguments); - this.set_image_maxwidth(); - + render_value: function() { var url; - if (value && value.substr(0, 10).indexOf(' ') == -1) { - url = 'data:image/png;base64,' + this.value; - } else { + if (this.get('value') && this.get('value').substr(0, 10).indexOf(' ') == -1) { + url = 'data:image/png;base64,' + this.get('value'); + } else if (this.get('value')) { url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' + this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime()); + } else { + url = "/web/static/src/img/placeholder.png"; } - this.$image.attr('src', url); - }, - set_image_maxwidth: function() { - this.$image.css('max-width', this.$element.width()); + var rendered = QWeb.render("FieldBinaryImage-img", {widget: this, url: url});; + this.$placeholder.html(rendered); }, on_file_change: function() { - this.set_image_maxwidth(); + this.render_value(); this._super.apply(this, arguments); }, on_file_uploaded_and_valid: function(size, name, content_type, file_base64) { - this.value = file_base64; + this.set({'value': file_base64}); this.binary_value = true; - this.$image.attr('src', 'data:' + (content_type || 'image/png') + ';base64,' + file_base64); + this.render_value(); }, on_clear: function() { this._super.apply(this, arguments); - this.$image.attr('src', '/web/static/src/img/placeholder.png'); + this.render_value(); } }); -openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ - form_template: "EmptyComponent", +instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ + template: "EmptyComponent", start: function() { this._super(); this.selected_value = null; this.render_list(); }, - set_value: function(value) { - this._super(value); - this.selected_value = value; + set_value: function(value_) { + this._super(value_); + this.selected_value = value_; this.render_list(); }, @@ -3420,7 +3750,7 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ }); } - var content = openerp.web.qweb.render("FieldStatus.content", {widget: this, _:_}); + var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_}); this.$element.html(content); var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}"); @@ -3457,77 +3787,36 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ } }); -openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({ - renderElement: function () { - var $root = $('<div class="oe_form_html_view">'); - this.render_children(this, $root); - var rendered = $root.html(); - this.$element.html(rendered); - }, - render_children: function (object, $into) { - var self = this, - fields = this.view.fields_view.fields; - _(object.children).each(function (child) { - if (typeof child === 'string') { - $into.text(child); - } else if (child.tag === 'field') { - var widget = instanciate_widget(self.view.registry.get_object('frame'), - self.view, {tag: 'ueule', attrs: {}, children: [child] }); - widget.appendTo($into); - } else { - var $child = $(document.createElement(child.tag)) - .attr(child.attrs) - .appendTo($into); - self.render_children(child, $child); - } - }); - } -}); - - /** - * Registry of form widgets, called by :js:`openerp.web.FormView` + * Registry of form widgets, called by :js:`instance.web.FormView` */ -openerp.web.form.widgets = new openerp.web.Registry({ - 'frame' : 'openerp.web.form.WidgetFrame', - 'group' : 'openerp.web.form.WidgetGroup', - 'notebook' : 'openerp.web.form.WidgetNotebook', - 'notebookpage' : 'openerp.web.form.WidgetNotebookPage', - 'separator' : 'openerp.web.form.WidgetSeparator', - 'label' : 'openerp.web.form.WidgetLabel', - 'button' : 'openerp.web.form.WidgetButton', - 'char' : 'openerp.web.form.FieldChar', - 'id' : 'openerp.web.form.FieldID', - 'email' : 'openerp.web.form.FieldEmail', - 'url' : 'openerp.web.form.FieldUrl', - 'text' : 'openerp.web.form.FieldText', - 'date' : 'openerp.web.form.FieldDate', - 'datetime' : 'openerp.web.form.FieldDatetime', - 'selection' : 'openerp.web.form.FieldSelection', - 'many2one' : 'openerp.web.form.FieldMany2One', - 'many2many' : 'openerp.web.form.FieldMany2Many', - 'one2many' : 'openerp.web.form.FieldOne2Many', - 'one2many_list' : 'openerp.web.form.FieldOne2Many', - 'reference' : 'openerp.web.form.FieldReference', - 'boolean' : 'openerp.web.form.FieldBoolean', - 'float' : 'openerp.web.form.FieldFloat', - 'integer': 'openerp.web.form.FieldFloat', - 'float_time': 'openerp.web.form.FieldFloat', - 'progressbar': 'openerp.web.form.FieldProgressBar', - 'image': 'openerp.web.form.FieldBinaryImage', - 'binary': 'openerp.web.form.FieldBinaryFile', - 'statusbar': 'openerp.web.form.FieldStatus', - 'html': 'openerp.web.form.WidgetHtml' +instance.web.form.widgets = new instance.web.Registry({ + 'char' : 'instance.web.form.FieldChar', + 'id' : 'instance.web.form.FieldID', + 'email' : 'instance.web.form.FieldEmail', + 'url' : 'instance.web.form.FieldUrl', + 'text' : 'instance.web.form.FieldText', + 'date' : 'instance.web.form.FieldDate', + 'datetime' : 'instance.web.form.FieldDatetime', + 'selection' : 'instance.web.form.FieldSelection', + 'many2one' : 'instance.web.form.FieldMany2One', + 'many2many' : 'instance.web.form.FieldMany2Many', + 'one2many' : 'instance.web.form.FieldOne2Many', + 'one2many_list' : 'instance.web.form.FieldOne2Many', + 'reference' : 'instance.web.form.FieldReference', + 'boolean' : 'instance.web.form.FieldBoolean', + 'float' : 'instance.web.form.FieldFloat', + 'integer': 'instance.web.form.FieldFloat', + 'float_time': 'instance.web.form.FieldFloat', + 'progressbar': 'instance.web.form.FieldProgressBar', + 'image': 'instance.web.form.FieldBinaryImage', + 'binary': 'instance.web.form.FieldBinaryFile', + 'statusbar': 'instance.web.form.FieldStatus' }); -var instanciate_widget = function(claz, view, node, o1, o2) { - var widget = new (claz)(view, node, o1, o2); - widget.element_class = (['formview', view.view_id, widget.element_name, - view.widgets_counter++].join("_")).replace(/[^\r\n\f0-9A-Za-z_-]/g, "_"); - view.widgets[widget.element_class] = widget; - return widget; -} - +instance.web.form.tags = new instance.web.Registry({ + 'button' : 'instance.web.form.WidgetButton', +}); }; diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index e8fff450c6e..c29957cac2d 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -1,9 +1,9 @@ -openerp.web.list = function (openerp) { -var _t = openerp.web._t, - _lt = openerp.web._lt; -var QWeb = openerp.web.qweb; -openerp.web.views.add('list', 'openerp.web.ListView'); -openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# */ { +openerp.web.list = function (instance) { +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; +instance.web.views.add('list', 'instance.web.ListView'); +instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListView# */ { _template: 'ListView', display_name: _lt('List'), defaults: { @@ -33,11 +33,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * See constructor parameters and method documentations for information on * the default behaviors and possible options for the list view. * - * @constructs openerp.web.ListView - * @extends openerp.web.View + * @constructs instance.web.ListView + * @extends instance.web.View * * @param parent parent object - * @param {openerp.web.DataSet} dataset the dataset the view should work with + * @param {instance.web.DataSet} dataset the dataset the view should work with * @param {String} view_id the listview's identifier, if any * @param {Object} options A set of options used to configure the view * @param {Boolean} [options.selectable=true] determines whether view rows are selectable (e.g. via a checkbox) @@ -61,12 +61,12 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.records = new Collection(); - this.set_groups(new openerp.web.ListView.Groups(this)); + this.set_groups(new instance.web.ListView.Groups(this)); - if (this.dataset instanceof openerp.web.DataSetStatic) { - this.groups.datagroup = new openerp.web.StaticDataGroup(this.dataset); + if (this.dataset instanceof instance.web.DataSetStatic) { + this.groups.datagroup = new instance.web.StaticDataGroup(this.dataset); } else { - this.groups.datagroup = new openerp.web.DataGroup( + this.groups.datagroup = new instance.web.DataGroup( this, this.model, dataset.get_domain(), dataset.get_context(), @@ -104,7 +104,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# /** * Set a custom Group construct as the root of the List View. * - * @param {openerp.web.ListView.Groups} groups + * @param {instance.web.ListView.Groups} groups */ set_groups: function (groups) { var self = this; @@ -136,7 +136,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @returns {$.Deferred} loading promise */ start: function() { - this._super(); this.$element.addClass('oe-listview'); return this.reload_view(null, null, true); }, @@ -208,6 +207,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.$element.html(QWeb.render(this._template, this)); // Head hook + // Selecting records this.$element.find('.all-record-selector').click(function(){ self.$element.find('.oe-record-selector input').prop('checked', self.$element.find('.all-record-selector').prop('checked') || false); @@ -216,70 +216,95 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# 'selected', [selection.ids, selection.records]); }); - this.$element.find('.oe-list-add') - .click(this.proxy('do_add_record')) - .attr('disabled', grouped && this.options.editable); - this.$element.find('.oe-list-delete') - .attr('disabled', true) - .click(this.proxy('do_delete_selected')); + // Sorting columns this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) { e.stopPropagation(); - var $this = $(this); self.dataset.sort($this.data('id')); if ($this.find('span').length) { - $this.find('span').toggleClass( - 'ui-icon-triangle-1-s ui-icon-triangle-1-n'); + $this.find('span').toggleClass( 'ui-icon-triangle-1-s ui-icon-triangle-1-n'); } else { - $this.append('<span class="ui-icon ui-icon-triangle-1-n">') - .siblings('.oe-sortable').find('span').remove(); + $this.append('<span class="ui-icon ui-icon-triangle-1-n">') .siblings('.oe-sortable').find('span').remove(); } self.reload_content(); }); - this.$element.find('.oe-list-pager') - .delegate('button', 'click', function () { - var $this = $(this); - switch ($this.data('pager-action')) { - case 'first': - self.page = 0; break; - case 'last': - self.page = Math.floor( - self.dataset.size() / self.limit()); - break; - case 'next': - self.page += 1; break; - case 'previous': - self.page -= 1; break; - } - self.reload_content(); - }).find('.oe-pager-state') - .click(function (e) { - e.stopPropagation(); + // Add and delete + if (!this.$buttons) { + this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self})); + if (this.options.$buttons) { + this.$buttons.appendTo(this.options.$buttons); + } else { + this.$element.find('.oe_list_buttons').replaceWith(this.$buttons); + } + this.$buttons.find('.oe_list_add') + .click(this.proxy('do_add_record')) + .prop('disabled', grouped && this.options.editable) + .end() + .find('.oe_list_delete') + .click(this.proxy('do_delete_selected')) + .prop('disabled', true); + } + + // Pager + if (!this.$pager) { + this.$pager = $(QWeb.render("ListView.pager", {'widget':self})); + if (this.options.$buttons) { + this.$pager.appendTo(this.options.$pager); + } else { + this.$element.find('.oe_list_pager').replaceWith(this.$pager); + } + + this.$pager + .on('click', 'a[data-pager-action]', function () { var $this = $(this); + var max_page = Math.floor(self.dataset.size() / self.limit()); + switch ($this.data('pager-action')) { + case 'first': + self.page = 0; break; + case 'last': + self.page = max_page - 1; + break; + case 'next': + self.page += 1; break; + case 'previous': + self.page -= 1; break; + } + if (self.page < 0) { + self.page = max_page; + } else if (self.page > max_page) { + self.page = 0; + } + self.reload_content(); + }).find('.oe-pager-state') + .click(function (e) { + e.stopPropagation(); + var $this = $(this); + + var $select = $('<select>') + .appendTo($this.empty()) + .click(function (e) {e.stopPropagation();}) + .append('<option value="80">80</option>' + + '<option value="100">100</option>' + + '<option value="200">200</option>' + + '<option value="500">500</option>' + + '<option value="NaN">' + _t("Unlimited") + '</option>') + .change(function () { + var val = parseInt($select.val(), 10); + self._limit = (isNaN(val) ? null : val); + self.page = 0; + self.reload_content(); + }) + .val(self._limit || 'NaN'); + }); + } - var $select = $('<select>') - .appendTo($this.empty()) - .click(function (e) {e.stopPropagation();}) - .append('<option value="80">80</option>' + - '<option value="100">100</option>' + - '<option value="200">200</option>' + - '<option value="500">500</option>' + - '<option value="NaN">' + _t("Unlimited") + '</option>') - .change(function () { - var val = parseInt($select.val(), 10); - self._limit = (isNaN(val) ? null : val); - self.page = 0; - self.reload_content(); - }) - .val(self._limit || 'NaN'); - }); - if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) { - this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); - this.sidebar.start(); + // Sidebar + if (!this.sidebar && this.options.$sidebar) { + this.sidebar = new instance.web.Sidebar(this); + this.sidebar.appendTo(this.options.$sidebar); this.sidebar.add_toolbar(this.fields_view.toolbar); - this.set_common_sidebar_sections(this.sidebar); } }, /** @@ -287,7 +312,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * * Horrifying side-effect: sets the dataset's data on this.dataset? * - * @param {openerp.web.DataSet} dataset + * @param {instance.web.DataSet} dataset */ configure_pager: function (dataset) { this.dataset.ids = dataset.ids; @@ -296,28 +321,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.dataset._length = dataset._length; } - var limit = this.limit(), - total = dataset.size(), - first = (this.page * limit), - last; - if (!limit || (total - first) < limit) { - last = total; - } else { - last = first + limit; - } - this.$element.find('span.oe-pager-state').empty().text(_.str.sprintf( - _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), { - first_record: first + 1, - last_record: last, - records_count: total - })); + var page = this.page + 1, + total = Math.floor(dataset.size() / this.limit()) + 1; - this.$element - .find('button[data-pager-action=first], button[data-pager-action=previous]') - .attr('disabled', this.page === 0) - .end() - .find('button[data-pager-action=last], button[data-pager-action=next]') - .attr('disabled', last === total); + this.$pager.find('.oe-pager-state').text(isNaN(total) + ? '-' : _.str.sprintf('%d / %d', page, total)); }, /** * Sets up the listview's columns: merges view and fields data, move @@ -328,7 +336,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @param {Boolean} [grouped] Should the grouping columns (group and count) be displayed */ setup_columns: function (fields, grouped) { - var domain_computer = openerp.web.form.compute_domain; + var domain_computer = instance.web.form.compute_domain; var noop = function () { return {}; }; var field_to_column = function (field) { @@ -397,7 +405,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * * The default implementation asks the list view's view manager to switch * to a different view (by calling - * :js:func:`~openerp.web.ViewManager.on_mode_switch`), using the + * :js:func:`~instance.web.ViewManager.on_mode_switch`), using the * provided record index (within the current list view's dataset). * * If the index is null, ``switch_to_record`` asks for the creation of a @@ -407,7 +415,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @param {String} [view="page"] the view type to switch to */ select_record:function (index, view) { - view = view || index == null ? 'form' : 'page'; + view = view || index == null ? 'form' : 'form'; this.dataset.index = index; _.delay(_.bind(function () { this.do_switch_view(view); @@ -418,12 +426,24 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# if (this.sidebar) { this.sidebar.$element.show(); } + if (this.$buttons) { + this.$buttons.show(); + } + if (this.$pager) { + this.$pager.show(); + } }, do_hide: function () { - this._super(); if (this.sidebar) { this.sidebar.$element.hide(); } + if (this.$buttons) { + this.$buttons.hide(); + } + if (this.$pager) { + this.$pager.hide(); + } + this._super(); }, /** * Reloads the list view based on the current settings (dataset & al) @@ -444,7 +464,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# view_id: this.view_id, view_type: "tree", context: this.dataset.get_context(context), - toolbar: this.options.sidebar + toolbar: !!this.options.$sidebar }, callback); } }, @@ -479,7 +499,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# reload: function () { return this.reload_content(); }, - do_load_state: function(state, warm) { var reload = false; if (state.page && this.page !== state.page) { @@ -507,7 +526,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# */ do_search: function (domain, context, group_by) { this.page = 0; - this.groups.datagroup = new openerp.web.DataGroup( + this.groups.datagroup = new instance.web.DataGroup( this, this.model, domain, context, group_by); this.groups.datagroup.sort = this.dataset._sort; @@ -544,16 +563,20 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @param {Array} records selected record values */ do_select: function (ids, records) { - this.$element.find('.oe-list-delete').attr('disabled', !ids.length); + this.$buttons.find('.oe_list_delete').attr('disabled', !ids.length); if (!ids.length) { this.dataset.index = 0; - if (this.sidebar) { this.sidebar.do_fold(); } + if (this.sidebar) { + this.sidebar.$element.hide(); + } this.compute_aggregates(); return; } this.dataset.index = _(this.dataset.ids).indexOf(ids[0]); - if (this.sidebar) { this.sidebar.do_unfold(); } + if (this.sidebar) { + this.sidebar.$element.show(); + } this.compute_aggregates(_(records).map(function (record) { return {count: 1, values: record}; @@ -575,7 +598,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# return; } - var c = new openerp.web.CompoundContext(); + var c = new instance.web.CompoundContext(); c.set_eval_context(_.extend({ active_id: id, active_ids: [id], @@ -592,7 +615,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * * @param {Number} index index of the record in the dataset * @param {Object} id identifier of the activated record - * @param {openerp.web.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups) + * @param {instance.web.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups) */ do_activate_record: function (index, id, dataset, view) { this.dataset.ids = dataset.ids; @@ -616,11 +639,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# /** * Computes the aggregates for the current list view, either on the * records provided or on the records of the internal - * :js:class:`~openerp.web.ListView.Group`, by calling - * :js:func:`~openerp.web.ListView.group.get_records`. + * :js:class:`~instance.web.ListView.Group`, by calling + * :js:func:`~instance.web.ListView.group.get_records`. * * Then displays the aggregates in the table through - * :js:method:`~openerp.web.ListView.display_aggregates`. + * :js:method:`~instance.web.ListView.display_aggregates`. * * @param {Array} [records] */ @@ -696,7 +719,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# } $footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id)) - .html(openerp.web.format_cell(aggregation, column, { + .html(instance.web.format_cell(aggregation, column, { process_modifiers: false })); }); @@ -747,9 +770,20 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# .attr('colspan', this.previous_colspan); this.previous_colspan = null; } + }, + no_result: function () { + if (this.groups.group_by + || !this.options.action + || !this.options.action.help) { + return; + } + this.$element.children('table').replaceWith( + $('<div class="oe_listview_nocontent">') + .append($('<img>', { src: '/web/static/src/img/list_empty_arrow.png' })) + .append($('<div>').html(this.options.action.help))); } }); -openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.ListView.List# */{ +instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{ /** * List display for the ListView, handles basic DOM events and transforms * them in the relevant higher-level events, to which the list view (or @@ -774,10 +808,10 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis * Triggered when a row of the table is clicked, provides the index (in * the rows array) and id of the selected record to the handle function. * - * @constructs openerp.web.ListView.List - * @extends openerp.web.Class + * @constructs instance.web.ListView.List + * @extends instance.web.Class * - * @param {Object} opts display options, identical to those of :js:class:`openerp.web.ListView` + * @param {Object} opts display options, identical to those of :js:class:`instance.web.ListView` */ init: function (group, opts) { var self = this; @@ -824,7 +858,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis this.records.bind(event, callback); }, this); - this.$_element = $('<tbody class="ui-widget-content">') + this.$_element = $('<tbody>') .appendTo(document.body) .delegate('th.oe-record-selector', 'click', function (e) { e.stopPropagation(); @@ -892,7 +926,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis // to get a correctly displayable value in the field var model = ref_match[1], id = parseInt(ref_match[2], 10); - new openerp.web.DataSet(this.view, model).name_get([id], function(names) { + new instance.web.DataSet(this.view, model).name_get([id], function(names) { if (!names.length) { return; } record.set(column.id, names[0][1]); }); @@ -907,14 +941,14 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis // fetch the name, set it on the record (in the right field) // and let the various registered events handle refreshing the // row - new openerp.web.DataSet(this.view, column.relation) + new instance.web.DataSet(this.view, column.relation) .name_get([value], function (names) { if (!names.length) { return; } record.set(column.id, names[0]); }); } } - return openerp.web.format_cell(record.toForm().data, column, { + return instance.web.format_cell(record.toForm().data, column, { model: this.dataset.model, id: record.get('id') }); @@ -1068,19 +1102,19 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis }); } }); -openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.ListView.Groups# */{ +instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.web.ListView.Groups# */{ passtrough_events: 'action deleted row_link', /** * Grouped display for the ListView. Handles basic DOM events and interacts - * with the :js:class:`~openerp.web.DataGroup` bound to it. + * with the :js:class:`~instance.web.DataGroup` bound to it. * * Provides events similar to those of - * :js:class:`~openerp.web.ListView.List` + * :js:class:`~instance.web.ListView.List` * - * @constructs openerp.web.ListView.Groups - * @extends openerp.web.Class + * @constructs instance.web.ListView.Groups + * @extends instance.web.Class * - * @param {openerp.web.ListView} view + * @param {instance.web.ListView} view * @param {Object} [options] * @param {Collection} [options.records] * @param {Object} [options.options] @@ -1191,7 +1225,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L self.records.proxy(group.value).reset(); delete self.children[group.value]; } - var child = self.children[group.value] = new openerp.web.ListView.Groups(self.view, { + var child = self.children[group.value] = new instance.web.ListView.Groups(self.view, { records: self.records.proxy(group.value), options: self.options, columns: self.columns @@ -1227,7 +1261,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L var group_column = _(self.columns).detect(function (column) { return column.id === group.grouped_on; }); try { - $group_column.html(openerp.web.format_cell( + $group_column.html(instance.web.format_cell( row_data, group_column, { value_if_empty: _t("Undefined"), process_modifiers: false @@ -1264,7 +1298,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L var r = {}; r[column.id] = {value: group.aggregates[column.id]}; $('<td class="oe-number">') - .html(openerp.web.format_cell( + .html(instance.web.format_cell( r, column, {process_modifiers: false})) .appendTo($row); } else { @@ -1297,7 +1331,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L }, render_dataset: function (dataset) { var self = this, - list = new openerp.web.ListView.List(this, { + list = new instance.web.ListView.List(this, { options: this.options, columns: this.columns, dataset: dataset, @@ -1344,6 +1378,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L self.records.add(records, {silent: true}); list.render(); d.resolve(list); + if (_.isEmpty(records)) { + view.no_result(); + } });}); return d.promise(); }, @@ -1510,10 +1547,10 @@ var Events = /** @lends Events# */{ return this; } }; -var Record = openerp.web.Class.extend(/** @lends Record# */{ +var Record = instance.web.Class.extend(/** @lends Record# */{ /** * @constructs Record - * @extends openerp.web.Class + * @extends instance.web.Class * * @mixes Events * @param {Object} [data] @@ -1591,7 +1628,7 @@ var Record = openerp.web.Class.extend(/** @lends Record# */{ } }); Record.include(Events); -var Collection = openerp.web.Class.extend(/** @lends Collection# */{ +var Collection = instance.web.Class.extend(/** @lends Collection# */{ /** * Smarter collections, with events, very strongly inspired by Backbone's. * @@ -1599,7 +1636,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ * various serious * * @constructs Collection - * @extends openerp.web.Class + * @extends instance.web.Class * * @mixes Events * @param {Array} [records] records to initialize the collection with @@ -1631,19 +1668,19 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ var records = record instanceof Array ? record : [record]; for(var i=0, length=records.length; i<length; ++i) { - var instance = (records[i] instanceof Record) ? records[i] : new Record(records[i]); - instance.bind(null, this._onRecordEvent); - this._byId[instance.get('id')] = instance; + var instance_ = (records[i] instanceof Record) ? records[i] : new Record(records[i]); + instance_.bind(null, this._onRecordEvent); + this._byId[instance_.get('id')] = instance_; if (options.at == undefined) { - this.records.push(instance); + this.records.push(instance_); if (!options.silent) { - this.trigger('add', this, instance, this.records.length-1); + this.trigger('add', this, instance_, this.records.length-1); } } else { var insertion_index = options.at + i; - this.records.splice(insertion_index, 0, instance); + this.records.splice(insertion_index, 0, instance_); if (!options.silent) { - this.trigger('add', this, instance, insertion_index); + this.trigger('add', this, instance_, insertion_index); } } this.length++; @@ -1774,7 +1811,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ } }); Collection.include(Events); -openerp.web.list = { +instance.web.list = { Events: Events, Record: Record, Collection: Collection diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index b8699bcc006..ac19e3f341d 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -2,16 +2,16 @@ * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out * @namespace */ -openerp.web.list_editable = function (openerp) { +openerp.web.list_editable = function (instance) { var KEY_RETURN = 13, KEY_ESCAPE = 27; - var QWeb = openerp.web.qweb; + var QWeb = instance.web.qweb; // editability status of list rows - openerp.web.ListView.prototype.defaults.editable = null; + instance.web.ListView.prototype.defaults.editable = null; // TODO: not sure second @lends on existing item is correct, to check - openerp.web.ListView.include(/** @lends openerp.web.ListView# */{ + instance.web.ListView.include(/** @lends instance.web.ListView# */{ init: function () { var self = this; this._super.apply(this, arguments); @@ -37,7 +37,7 @@ openerp.web.list_editable = function (openerp) { * * @param {Number} index index of the record in the dataset * @param {Object} id identifier of the record being edited - * @param {openerp.web.DataSet} dataset dataset in which the record is available + * @param {instance.web.DataSet} dataset dataset in which the record is available */ do_edit: function (index, id, dataset) { _.extend(this.dataset, dataset); @@ -93,8 +93,8 @@ openerp.web.list_editable = function (openerp) { } }); - openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{ - passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved", + instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{ + passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved", new_record: function () { // TODO: handle multiple children this.children[null].new_record(); @@ -113,7 +113,7 @@ openerp.web.list_editable = function (openerp) { } }); - openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{ + instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{ row_clicked: function (event) { if (!this.options.editable) { return this._super.apply(this, arguments); @@ -235,7 +235,8 @@ openerp.web.list_editable = function (openerp) { } self.edition = true; self.edition_id = record_id; - self.edition_form = new openerp.web.ListEditableFormView(self.view, self.dataset, false); + $new_row.addClass("oe_form_container"); + self.edition_form = new instance.web.ListEditableFormView(self.view, self.dataset, false); self.edition_form.$element = $new_row; self.edition_form.editable_list = self; // HO HO @@ -273,7 +274,7 @@ openerp.web.list_editable = function (openerp) { // insert after the source record var index = self.records.indexOf( self.records.get(source_record_id)) + 1; - record = new openerp.web.list.Record({id: id}); + record = new instance.web.list.Record({id: id}); self.records.add(record, {at: index}); self.dataset.ids.splice(index, 0, id); } @@ -366,52 +367,57 @@ openerp.web.list_editable = function (openerp) { }); } }); - if (!openerp.web.list) { - openerp.web.list = {}; - } - if (!openerp.web.list.form) { - openerp.web.list.form = {}; - } - openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({ - form_template: 'ListView.row.frame' - }); - var form_widgets = openerp.web.form.widgets; - openerp.web.list.form.widgets = form_widgets.extend({ - 'frame': 'openerp.web.list.form.WidgetFrame' - }); - // All form widgets inherit a problematic behavior from - // openerp.web.form.WidgetFrame: the cell itself is removed when invisible - // whether it's @invisible or @attrs[invisible]. In list view, only the - // former should completely remove the cell. We need to override update_dom - // on all widgets since we can't just hit on widget itself (I think) - var list_form_widgets = openerp.web.list.form.widgets; - _(form_widgets.map).each(function (widget_path, key) { - if (key === 'frame') { return; } - var new_path = 'openerp.web.list.form.' + key; - - openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({ - update_dom: function () { - this.$element.children().css('visibility', ''); - if (this.modifiers.tree_invisible) { - var old_invisible = this.invisible; - this.invisible = true; - this._super(); - this.invisible = old_invisible; - } else if (this.invisible) { - this.$element.children().css('visibility', 'hidden'); - } else { - this._super(); - } - } - }); - list_form_widgets.add(key, new_path); - }); - openerp.web.ListEditableFormView = openerp.web.FormView.extend({ - form_template: 'ListView.row.form', + instance.web.ListEditableFormView = instance.web.FormView.extend({ init: function() { - this._super.apply(this, arguments); - this.registry = openerp.web.list.form.widgets; + this._super.apply(this, arguments); + this.rendering_engine = new instance.web.ListEditableRenderingEngine(this); + }, + renderElement: function() {} + }); + + instance.web.ListEditableRenderingEngine = instance.web.Class.extend({ + init: function(view) { + this.view = view; + }, + set_fields_view: function(fields_view) { + this.fvg = fields_view; + }, + set_tags_registry: function(tags_registry) { + this.tags_registry = tags_registry; + }, + set_fields_registry: function(fields_registry) { + this.fields_registry = fields_registry; + }, + render_to: function($element) { + var self = this; + + var xml = instance.web.json_node_to_xml(this.fvg.arch); + var $xml = $(xml); + + if (this.view.editable_list.options.selectable) + $("<td>").appendTo($element); + if (this.view.editable_list.options.isClarkGable) + $("<td>").appendTo($element); + + $xml.children().each(function(i, el) { + var modifiers = JSON.parse($(el).attr("modifiers") || "{}"); + var $td = $("<td>"); + if (modifiers.tree_invisible === true) + $td.hide(); + var tag_name = el.tagName.toLowerCase(); + var key = tag_name; + if (tag_name === "field") { + var name = $(el).attr("name"); + key = $(el).attr('widget') || self.fvg.fields[name].type; + } + var obj = self.view.fields_registry.get_object(key); + var w = new (obj)(self.view, instance.web.xml_to_json(el)); + self.view.register_field(w, $(el).attr("name")); + w.appendTo($td); + $td.appendTo($element); + }); + $("<td><button class='oe-edit-row-save' type='button'></button></td>").appendTo($element); }, }); }; diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js deleted file mode 100644 index 8949169838a..00000000000 --- a/addons/web/static/src/js/view_page.js +++ /dev/null @@ -1,288 +0,0 @@ -openerp.web.page = function (openerp) { - var _t = openerp.web._t, - _lt = openerp.web._lt; - - openerp.web.views.add('page', 'openerp.web.PageView'); - openerp.web.PageView = openerp.web.FormView.extend({ - form_template: "PageView", - display_name: _lt('Page'), - init: function () { - this._super.apply(this, arguments); - this.registry = openerp.web.page.readonly; - }, - reload: function () { - if (this.dataset.index == null) { - this.do_prev_view(); - return $.Deferred().reject().promise(); - } - return this._super(); - }, - on_loaded: function(data) { - this._super(data); - this.$form_header.find('button.oe_form_button_edit').click(this.on_button_edit); - this.$form_header.find('button.oe_form_button_create').click(this.on_button_create); - this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate); - this.$form_header.find('button.oe_form_button_delete').click(this.on_button_delete); - }, - on_button_edit: function() { - return this.do_switch_view('form'); - }, - on_button_create: function() { - this.dataset.index = null; - return this.do_switch_view('form'); - }, - on_button_duplicate: function() { - var self = this; - var def = $.Deferred(); - $.when(this.has_been_loaded).then(function() { - self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { - return self.on_created({ result : new_id }); - }).then(function() { - return self.do_switch_view('form'); - }).then(function() { - def.resolve(); - }); - }); - return def.promise(); - }, - on_button_delete: function() { - var self = this; - var def = $.Deferred(); - $.when(this.has_been_loaded).then(function() { - if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) { - self.dataset.unlink([self.datarecord.id]).then(function() { - self.on_pager_action('next'); - def.resolve(); - }); - } else { - $.async_when().then(function () { - def.reject(); - }) - } - }); - return def.promise(); - } - }); - - /** @namespace */ - openerp.web.page = {}; - - openerp.web.page.WidgetFrameReadonly = openerp.web.form.WidgetFrame.extend({ - form_template: 'WidgetFrame.readonly' - }); - openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({ - - }); - openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({ - form_template: 'FieldChar.readonly', - init: function(view, node) { - this._super(view, node); - this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1'; - }, - set_value: function (value) { - this._super.apply(this, arguments); - var show_value = openerp.web.format_value(value, this, ''); - if (this.password) { - show_value = new Array(show_value.length + 1).join('*'); - } - this.$element.find('div').text(show_value); - return show_value; - } - }); - openerp.web.page.FieldFloatReadonly = openerp.web.page.FieldCharReadonly.extend({ - init: function (view, node) { - this._super(view, node); - if (node.attrs.digits) { - this.digits = py.eval(node.attrs.digits); - } else { - this.digits = view.fields_view.fields[node.attrs.name].digits; - } - } - }); - openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({ - form_template: 'FieldURI.readonly', - scheme: null, - format_value: function (value) { - return value; - }, - set_value: function (value) { - if (!value) { - this.$element.find('a').text('').attr('href', '#'); - return; - } - this.$element.find('a') - .attr('href', this.scheme + ':' + value) - .text(this.format_value(value)); - } - }); - openerp.web.page.FieldEmailReadonly = openerp.web.page.FieldURIReadonly.extend({ - scheme: 'mailto' - }); - openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({ - set_value: function (value) { - if (!value) { - this.$element.find('a').text('').attr('href', '#'); - return; - } - var s = /(\w+):(.+)/.exec(value); - if (!s) { - value = "http://" + value; - } - this.$element.find('a').attr('href', value).text(value); - } - }); - openerp.web.page.FieldBooleanReadonly = openerp.web.form.FieldBoolean.extend({ - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('input').prop('disabled', true); - } - }); - openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({ - form_template: 'FieldChar.readonly', - init: function(view, node) { - // lifted straight from r/w version - var self = this; - this._super(view, node); - this.values = _.clone(this.field.selection); - _.each(this.values, function(v, i) { - if (v[0] === false && v[1] === '') { - self.values.splice(i, 1); - } - }); - this.values.unshift([false, '']); - }, - set_value: function (value) { - value = value === null ? false : value; - value = value instanceof Array ? value[0] : value; - var option = _(this.values) - .detect(function (record) { return record[0] === value; }); - this._super(value); - this.$element.find('div').text(option ? option[1] : this.values[0][1]); - } - }); - openerp.web.page.FieldMany2OneReadonly = openerp.web.page.FieldURIReadonly.extend({ - set_value: function (value) { - value = value || null; - this.invalid = false; - var self = this; - this.value = value; - self.update_dom(); - self.on_value_changed(); - var real_set_value = function(rval) { - self.value = rval; - self.$element.find('a') - .unbind('click') - .text(rval ? rval[1] : '') - .click(function () { - self.do_action({ - type: 'ir.actions.act_window', - res_model: self.field.relation, - res_id: self.value[0], - context: self.build_context(), - views: [[false, 'page'], [false, 'form']], - target: 'current' - }); - return false; - }); - }; - if (value && !(value instanceof Array)) { - new openerp.web.DataSet( - this, this.field.relation, self.build_context()) - .name_get([value], function(data) { - real_set_value(data[0]); - }); - } else { - $.async_when().then(function() {real_set_value(value);}); - } - }, - get_value: function() { - if (!this.value) { - return false; - } else if (this.value instanceof Array) { - return this.value[0]; - } else { - return this.value; - } - } - }); - openerp.web.page.FieldReferenceReadonly = openerp.web.page.FieldMany2OneReadonly.extend({ - set_value: function (value) { - if (!value) { - return this._super(null); - } - var reference = value.split(','); - this.field.relation = reference[0]; - var id = parseInt(reference[1], 10); - return this._super(id); - }, - get_value: function () { - if (!this.value) { - return null; - } - var id; - if (typeof this.value === 'number') { - // name_get has not run yet - id = this.value; - } else { - id = this.value[0]; - } - return _.str.sprintf('%s,%d', this.field.relation, id); - } - }); - - openerp.web.page.FieldMany2ManyReadonly = openerp.web.form.FieldMany2Many.extend({ - force_readonly: true - }); - openerp.web.page.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({ - force_readonly: true - }); - openerp.web.page.FieldBinaryImageReaonly = openerp.web.form.FieldBinaryImage.extend({ - update_dom: function() { - this._super.apply(this, arguments); - this.$element.find('.oe-binary').hide(); - } - }); - openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({ - form_template: 'FieldURI.readonly', - start: function() { - this._super.apply(this, arguments); - var self = this; - this.$element.find('a').click(function() { - if (self.value) { - self.on_save_as(); - } - return false; - }); - }, - set_value: function(value) { - this._super.apply(this, arguments); - this.$element.find('a').show(!!value); - if (value) { - var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || ''); - this.$element.find('a').text(show_value); - } - } - }); - openerp.web.page.readonly = openerp.web.form.widgets.extend({ - 'frame': 'openerp.web.page.WidgetFrameReadonly', - 'char': 'openerp.web.page.FieldCharReadonly', - 'id': 'openerp.web.page.FieldCharReadonly', - 'email': 'openerp.web.page.FieldEmailReadonly', - 'url': 'openerp.web.page.FieldUrlReadonly', - 'text': 'openerp.web.page.FieldCharReadonly', - 'date': 'openerp.web.page.FieldCharReadonly', - 'datetime': 'openerp.web.page.FieldCharReadonly', - 'selection' : 'openerp.web.page.FieldSelectionReadonly', - 'many2one': 'openerp.web.page.FieldMany2OneReadonly', - 'many2many' : 'openerp.web.page.FieldMany2ManyReadonly', - 'one2many' : 'openerp.web.page.FieldOne2ManyReadonly', - 'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly', - 'reference': 'openerp.web.page.FieldReferenceReadonly', - 'boolean': 'openerp.web.page.FieldBooleanReadonly', - 'float': 'openerp.web.page.FieldFloatReadonly', - 'integer': 'openerp.web.page.FieldCharReadonly', - 'float_time': 'openerp.web.page.FieldCharReadonly', - 'binary': 'openerp.web.page.FieldBinaryFileReadonly', - 'image': 'openerp.web.page.FieldBinaryImageReaonly' - }); -}; diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js index 6039e4a0822..9e938fe293a 100644 --- a/addons/web/static/src/js/view_tree.js +++ b/addons/web/static/src/js/view_tree.js @@ -2,12 +2,12 @@ * OpenERP web library *---------------------------------------------------------*/ -openerp.web.view_tree = function(openerp) { -var QWeb = openerp.web.qweb, - _lt = openerp.web._lt; +openerp.web.view_tree = function(instance) { +var QWeb = instance.web.qweb, + _lt = instance.web._lt; -openerp.web.views.add('tree', 'openerp.web.TreeView'); -openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# */{ +instance.web.views.add('tree', 'instance.web.TreeView'); +instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeView# */{ display_name: _lt('Tree'), /** * Indicates that this view is not searchable, and thus that no search @@ -17,8 +17,8 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# /** * Genuine tree view (the one displayed as a tree, not the list) * - * @constructs openerp.web.TreeView - * @extends openerp.web.View + * @constructs instance.web.TreeView + * @extends instance.web.View * * @param parent * @param dataset @@ -39,7 +39,6 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# }, start: function () { - this._super(); return this.rpc("/web/treeview/load", { model: this.model, view_id: this.view_id, @@ -204,7 +203,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# 'fields_view': self.fields_view.arch.children, 'fields': self.fields, 'level': $curr_node.data('level') || 0, - 'render': openerp.web.format_value, + 'render': instance.web.format_value, 'color_for': self.color_for }); @@ -227,12 +226,12 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# return this.rpc('/web/treeview/action', { id: id, model: this.dataset.model, - context: new openerp.web.CompoundContext( + context: new instance.web.CompoundContext( this.dataset.get_context(), local_context) }).pipe(function (actions) { if (!actions.length) { return; } var action = actions[0][2]; - var c = new openerp.web.CompoundContext(local_context); + var c = new instance.web.CompoundContext(local_context); if (action.context) { c.add(action.context); } diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 43ecc8444fc..36e3c2b802b 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -2,21 +2,11 @@ * OpenERP web library *---------------------------------------------------------*/ -openerp.web.views = function(session) { -var QWeb = session.web.qweb, - _t = session.web._t; +openerp.web.views = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; -/** - * Registry for all the client actions key: tag value: widget - */ -session.web.client_actions = new session.web.Registry(); - -/** - * Registry for all the main views - */ -session.web.views = new session.web.Registry(); - -session.web.ActionManager = session.web.OldWidget.extend({ +instance.web.ActionManager = instance.web.Widget.extend({ init: function(parent) { this._super(parent); this.inner_action = null; @@ -25,9 +15,6 @@ session.web.ActionManager = session.web.OldWidget.extend({ this.dialog_viewmanager = null; this.client_widget = null; }, - render: function() { - return '<div id="' + this.element_id + '" style="height: 100%;"></div>'; - }, dialog_stop: function () { if (this.dialog) { this.dialog_viewmanager.destroy(); @@ -65,8 +52,8 @@ session.web.ActionManager = session.web.OldWidget.extend({ if (run_action) { this.null_action(); action_loaded = this.do_action(state.action_id); - session.webclient.menu.has_been_loaded.then(function() { - session.webclient.menu.open_action(state.action_id); + instance.webclient.menu.has_been_loaded.then(function() { + instance.webclient.menu.open_action(state.action_id); }); } } else if (state.model && state.id) { @@ -76,7 +63,7 @@ session.web.ActionManager = session.web.OldWidget.extend({ res_model: state.model, res_id: state.id, type: 'ir.actions.act_window', - views: [[false, 'page'], [false, 'form']] + views: [[false, 'form']] }; action_loaded = this.do_action(action); } else if (state.sa) { @@ -112,12 +99,13 @@ session.web.ActionManager = session.web.OldWidget.extend({ } var type = action.type.replace(/\./g,'_'); var popup = action.target === 'new'; + var inline = action.target === 'inline'; action.flags = _.extend({ - views_switcher : !popup, - search_view : !popup, - action_buttons : !popup, - sidebar : !popup, - pager : !popup, + views_switcher : !popup && !inline, + search_view : !popup && !inline, + action_buttons : !popup && !inline, + sidebar : !popup && !inline, + pager : !popup && !inline, display_title : !popup }, action.flags || {}); if (!(type in this)) { @@ -136,31 +124,31 @@ session.web.ActionManager = session.web.OldWidget.extend({ .contains(action.res_model)) { var old_close = on_close; on_close = function () { - session.webclient.do_reload().then(old_close); + instance.webclient.do_reload().then(old_close); }; } if (action.target === 'new') { if (this.dialog == null) { - this.dialog = new session.web.Dialog(this, { width: '80%' }); + this.dialog = new instance.web.Dialog(this, { width: '80%' }); if(on_close) this.dialog.on_close.add(on_close); } else { this.dialog_viewmanager.destroy(); } this.dialog.dialog_title = action.name; - this.dialog_viewmanager = new session.web.ViewManagerAction(this, action); + this.dialog_viewmanager = new instance.web.ViewManagerAction(this, action); this.dialog_viewmanager.appendTo(this.dialog.$element); this.dialog.open(); } else { if(action.menu_id) { return this.getParent().do_action(action, function () { - session.webclient.menu.open_menu(action.menu_id); + instance.webclient.menu.open_menu(action.menu_id); }); } this.dialog_stop(); this.content_stop(); this.inner_action = action; - this.inner_viewmanager = new session.web.ViewManagerAction(this, action); + this.inner_viewmanager = new instance.web.ViewManagerAction(this, action); this.inner_viewmanager.appendTo(this.$element); } }, @@ -182,7 +170,7 @@ session.web.ActionManager = session.web.OldWidget.extend({ ir_actions_client: function (action) { this.content_stop(); this.dialog_stop(); - var ClientWidget = session.web.client_actions.get_object(action.tag); + var ClientWidget = instance.web.client_actions.get_object(action.tag); (this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element); }, ir_actions_report_xml: function(action, on_closed) { @@ -204,7 +192,7 @@ session.web.ActionManager = session.web.OldWidget.extend({ } self.dialog_stop(); }, - error: session.webclient.crashmanager.on_rpc_error + error: instance.webclient.crashmanager.on_rpc_error }) }); }, @@ -216,16 +204,8 @@ session.web.ActionManager = session.web.OldWidget.extend({ } }); -session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.ViewManager# */{ +instance.web.ViewManager = instance.web.Widget.extend({ template: "ViewManager", - /** - * @constructs session.web.ViewManager - * @extends session.web.OldWidget - * - * @param parent - * @param dataset - * @param views - */ init: function(parent, dataset, views, flags) { this._super(parent); this.model = dataset ? dataset.model : undefined; @@ -234,7 +214,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V this.active_view = null; this.views_src = _.map(views, function(x) { if (x instanceof Array) { - var View = session.web.views.get_object(x[1], true); + var View = instance.web.views.get_object(x[1], true); return { view_id: x[0], view_type: x[1], @@ -246,22 +226,16 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V }); this.views = {}; this.flags = flags || {}; - this.registry = session.web.views; + this.registry = instance.web.views; this.views_history = []; }, - render: function() { - return session.web.qweb.render(this.template, { - self: this, - prefix: this.element_id, - views: this.views_src}); - }, /** * @returns {jQuery.Deferred} initial view loading promise */ start: function() { this._super(); var self = this; - this.$element.find('.oe_vm_switch button').click(function() { + this.$element.find('.oe_view_manager_switch a').click(function() { self.on_mode_switch($(this).data('view-type')); }); var views_ids = {}; @@ -270,7 +244,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V deferred : $.Deferred(), controller : null, options : _.extend({ - sidebar_id : self.element_id + '_sidebar_' + view.view_type, + $buttons : self.$element.find('.oe_view_manager_buttons'), + $sidebar : self.$element.find('.oe_view_manager_sidebar'), + $pager : self.$element.find('.oe_view_manager_pager'), action : self.action, action_views_ids : views_ids }, self.flags, self.flags[view.view_type] || {}, view.options || {}) @@ -278,7 +254,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V views_ids[view.view_type] = view.view_id; }); if (this.flags.views_switcher === false) { - this.$element.find('.oe_vm_switch').hide(); + this.$element.find('.oe_view_manager_switch').hide(); } // If no default view defined, switch to the first one in sequence var default_view = this.flags.default_view || this.views_src[0].view_type; @@ -292,9 +268,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V * @returns {jQuery.Deferred} new view loading promise */ on_mode_switch: function(view_type, no_store) { - var self = this, - view = this.views[view_type], - view_promise; + var self = this; + var view = this.views[view_type]; + var view_promise; if(!view) return $.Deferred().reject(); @@ -306,13 +282,22 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V if (!view.controller) { // Lazy loading of views var controllerclass = this.registry.get_object(view_type); - var controller = new controllerclass(this, this.dataset, view.view_id, view.options); + var options = _.clone(view.options); + if (view_type === "form" && this.action) { + switch (this.action.target) { + case 'new': + case 'inline': + options.initial_mode = 'edit'; + break; + } + } + var controller = new controllerclass(this, this.dataset, view.view_id, options); if (view.embedded_view) { controller.set_embedded_view(view.embedded_view); } controller.do_switch_view.add_last(this.on_mode_switch); controller.do_prev_view.add_last(this.on_prev_view); - var container = $("#" + this.element_id + '_view_' + view_type); + var container = this.$element.find(".oe_view_manager_view_" + view_type); view_promise = controller.appendTo(container); this.views[view_type].controller = controller; this.views[view_type].deferred.resolve(view_type); @@ -335,9 +320,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V } this.$element - .find('.oe_vm_switch button').removeAttr('disabled') - .filter('[data-view-type="' + view_type + '"]') - .attr('disabled', true); + .find('.oe_view_manager_switch a').parent().removeClass('active') + this.$element + .find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]') + .parent().addClass('active'); $.when(view_promise).then(function () { _.each(_.keys(self.views), function(view_name) { @@ -367,15 +353,16 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V * @returns {$.Deferred} switching end signal */ on_prev_view: function (options) { + options = options || {}; var current_view = this.views_history.pop(); var previous_view = this.views_history[this.views_history.length - 1] || options['default']; if (options.created && current_view === 'form' && previous_view === 'list') { // APR special case: "If creation mode from list (and only from a list), // after saving, go to page view (don't come back in list)" - return this.on_mode_switch('page'); + return this.on_mode_switch('form'); } else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') { // APR special case: "If creation from dashboard, we have no previous view - return this.on_mode_switch('page'); + return this.on_mode_switch('form'); } return this.on_mode_switch(previous_view, true); }, @@ -390,12 +377,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V if (this.searchview) { this.searchview.destroy(); } - this.searchview = new session.web.SearchView( - this, this.dataset, - view_id, search_defaults, this.flags.search_view === false); + this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false); this.searchview.on_search.add(this.do_searchview_search); - return this.searchview.appendTo($("#" + this.element_id + "_search")); + return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search")); }, do_searchview_search: function(domains, contexts, groupbys) { var self = this, @@ -451,13 +436,13 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V } }); -session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{ +instance.web.ViewManagerAction = instance.web.ViewManager.extend({ template:"ViewManagerAction", /** - * @constructs session.web.ViewManagerAction - * @extends session.web.ViewManager + * @constructs instance.web.ViewManagerAction + * @extends instance.web.ViewManager * - * @param {session.web.ActionManager} parent parent object/widget + * @param {instance.web.ActionManager} parent parent object/widget * @param {Object} action descriptor for the action this viewmanager needs to manage its views. */ init: function(parent, action) { @@ -482,7 +467,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner this._super(parent, null, action.views, flags); this.session = parent.session; this.action = action; - var dataset = new session.web.DataSetSearch(this, action.res_model, action.context, action.domain); + var dataset = new instance.web.DataSetSearch(this, action.res_model, action.context, action.domain); if (action.res_id) { dataset.ids.push(action.res_id); dataset.index = 0; @@ -523,7 +508,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner this.$element.find('.oe_debug_view').change(this.on_debug_changed); if (this.action.help && !this.flags.low_profile) { - var Users = new session.web.DataSet(self, 'res.users'), + var Users = new instance.web.DataSet(self, 'res.users'), $tips = this.$element.find('.oe_view_manager_menu_tips'); $tips.delegate('blockquote button', 'click', function() { var $this = $(this); @@ -547,17 +532,6 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } } - var $res_logs = this.$element.find('.oe-view-manager-logs:first'); - $res_logs.delegate('a.oe-more-logs', 'click', function () { - $res_logs.removeClass('oe-folded'); - return false; - }).delegate('a.oe-remove-everything', 'click', function () { - $res_logs.removeClass('oe-has-more').find('ul').empty(); - $res_logs.css('display','none'); - return false; - }); - $res_logs.css('display','none'); - return manager_ready; }, on_debug_changed: function (evt) { @@ -568,23 +542,26 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner current_view = this.views[this.active_view].controller; switch (val) { case 'fvg': - var dialog = new session.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open(); - $('<pre>').text(session.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element); + var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open(); + $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element); break; case 'perm_read': var ids = current_view.get_selected_ids(); if (ids.length === 1) { this.dataset.call('perm_read', [ids]).then(function(result) { - var dialog = new session.web.Dialog(this, { + var dialog = new instance.web.Dialog(this, { title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model), width: 400 }, QWeb.render('ViewManagerDebugViewLog', { perm : result[0], - format : session.web.format_value + format : instance.web.format_value })).open(); }); } break; + case 'toggle_layout_outline': + current_view.rendering_engine.toggle_layout_debugging(); + break; case 'fields': this.dataset.call_and_eval( 'fields_get', [false, {}], null, 1).then(function (fields) { @@ -602,7 +579,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner .append($('<dd style="white-space: pre-wrap;">').text(def)); }); }); - new session.web.Dialog(self, { + new instance.web.Dialog(self, { title: _.str.sprintf(_t("Model %s fields"), self.dataset.model), width: '95%'}, $root).open(); @@ -610,7 +587,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner break; case 'manage_views': if (current_view.fields_view && current_view.fields_view.arch) { - var view_editor = new session.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch); + var view_editor = new instance.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch); view_editor.start(); } else { this.do_warn(_t("Manage Views"), @@ -661,8 +638,6 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner return $.when(this._super(view_type, no_store)).then(function () { self.shortcut_check(self.views[view_type]); - self.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').removeClass('oe-has-more').css('display','none').find('ul').empty(); - var controller = self.views[self.active_view].controller, fvg = controller.fields_view, view_id = (fvg && fvg.view_id) || '--'; @@ -744,127 +719,88 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } }); }, - /** - * Intercept do_action resolution from children views - */ - on_action_executed: function () { - return new session.web.DataSet(this, 'res.log') - .call('get', [], this.do_display_log); - }, - /** - * @param {Array<Object>} log_records - */ - do_display_log: function (log_records) { - var self = this; - var cutoff = 3; - var $logs = this.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').css('display', 'block'); - var $logs_list = $logs.find('ul').empty(); - $logs.toggleClass('oe-has-more', log_records.length > cutoff); - _(log_records.reverse()).each(function (record) { - var context = {}; - if (record.context) { - try { context = py.eval(record.context); } - catch (e) { /* TODO: what do I do now? */ } - } - $(_.str.sprintf('<li><a href="#">%s</a></li>', record.name)) - .appendTo($logs_list) - .delegate('a', 'click', function () { - self.do_action({ - type: 'ir.actions.act_window', - res_model: record.res_model, - res_id: record.res_id, - // TODO: need to have an evaluated context here somehow - context: context, - views: [[context.view_id || false, 'form']] - }); - return false; - }); - }); - }, display_title: function () { return this.action.name; } }); -session.web.Sidebar = session.web.OldWidget.extend({ - init: function(parent, element_id) { - this._super(parent, element_id); - this.items = {}; - this.sections = {}; +instance.web.Sidebar = instance.web.Widget.extend({ + init: function(parent) { + this._super(parent); + var view = this.getParent(); + this.sections = [ + { 'name' : 'print', 'label' : _t('Print'), }, + { 'name' : 'files', 'label' : _t('Attachement'), }, + { 'name' : 'other', 'label' : _t('More'), } + ]; + this.items = { + 'print' : [], + 'files' : [], + 'other' : [ + { label: _t("Import"), callback: view.on_sidebar_import }, + { label: _t("Export"), callback: view.on_sidebar_export } + ] + } + if (this.session.uid === 1) { + var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") }; + this.items.other.push(item); + } }, start: function() { - this._super(this); var self = this; - this.$element.html(session.web.qweb.render('Sidebar')); - this.$element.find(".toggle-sidebar").click(function(e) { - self.do_toggle(); + this._super(this); + this.redraw(); + this.$element.on('click','.oe_dropdown_toggle',function(event) { + $(this).parent().find('ul').toggle(); + return false; }); - }, - add_default_sections: function() { - var self = this, - view = this.getParent(), - view_manager = view.getParent(), - action = view_manager.action; - if (this.session.uid === 1) { - this.add_section(_t('Customize'), 'customize'); - this.add_items('customize', [{ - label: _t("Translate"), - callback: view.on_sidebar_translate, - title: _t("Technical translation") - }]); - } - - this.add_section(_t('Other Options'), 'other'); - this.add_items('other', [ - { - label: _t("Import"), - callback: view.on_sidebar_import - }, { - label: _t("Export"), - callback: view.on_sidebar_export + this.$element.on('click','.oe_dropdown_menu li a', function(event) { + var section = $(this).data('section'); + var index = $(this).data('index'); + $(this).closest('ul').hide(); + var item = self.items[section][index]; + if (item.callback) { + item.callback.apply(self, [item]); + } else if (item.action) { + self.on_item_action_clicked(item); + } else if (item.url) { + return true; } - ]); + return false; + }); + //this.$div.html(QWeb.render('FormView.sidebar.attachments', this)); + //this.$element.find('.oe-binary-file').change(this.on_attachment_changed); + //this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete); + }, + redraw: function() { + var self = this; + self.$element.html(QWeb.render('Sidebar', {widget: self})); + this.$element.find('ul').hide(); + }, + add_section: function() { + var self = this; }, - add_toolbar: function(toolbar) { var self = this; - _.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) { - var items = toolbar[type[0]]; - if (items.length) { + _.each(['print','action','relate'], function(type) { + var items = toolbar[type]; + if (items) { for (var i = 0; i < items.length; i++) { items[i] = { label: items[i]['name'], action: items[i], - classname: 'oe_sidebar_' + type[0] + classname: 'oe_sidebar_' + type } } - self.add_section(type[1], type[0]); - self.add_items(type[0], items); + self.add_items(type=='print' ? 'print' : 'other', items); } }); }, - - add_section: function(name, code) { - if(!code) code = _.str.underscored(name); - var $section = this.sections[code]; - - if(!$section) { - var section_id = _.uniqueId(this.element_id + '_section_' + code + '_'); - $section = $(session.web.qweb.render("Sidebar.section", { - section_id: section_id, - name: name, - classname: 'oe_sidebar_' + code - })); - $section.appendTo(this.$element.find('div.sidebar-actions')); - this.sections[code] = $section; - } - return $section; - }, /** * For each item added to the section: * * ``label`` - * will be used as the item's name in the sidebar + * will be used as the item's name in the sidebar, can be html * * ``action`` * descriptor for the action which will be executed, ``action`` and @@ -886,34 +822,10 @@ session.web.Sidebar = session.web.OldWidget.extend({ * @param {Array<{label, action | callback[, classname][, title]}>} items */ add_items: function(section_code, items) { - var self = this, - $section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code), - section_id = $section.attr('id'); - + var self = this; if (items) { - for (var i = 0; i < items.length; i++) { - items[i].element_id = _.uniqueId(section_id + '_item_'); - this.items[items[i].element_id] = items[i]; - } - - var $items = $(session.web.qweb.render("Sidebar.section.items", {items: items})); - - $items.find('a.oe_sidebar_action_a').click(function() { - var item = self.items[$(this).attr('id')]; - if (item.callback) { - item.callback.apply(self, [item]); - } - if (item.action) { - self.on_item_action_clicked(item); - } - return false; - }); - - var $ul = $section.find('ul'); - if(!$ul.length) { - $ul = $('<ul/>').appendTo($section); - } - $items.appendTo($ul); + this.items[section_code].push.apply(this.items[section_code],items); + this.redraw(); } }, on_item_action_clicked: function(item) { @@ -921,11 +833,7 @@ session.web.Sidebar = session.web.OldWidget.extend({ self.getParent().sidebar_context().then(function (context) { var ids = self.getParent().get_selected_ids(); if (ids.length == 0) { - //TODO: make prettier warning? - openerp.web.dialog($("<div />").text(_t("You must choose at least one record.")), { - title: _t("Warning"), - modal: true - }); + instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true }); return false; } var additional_context = _.extend({ @@ -948,18 +856,60 @@ session.web.Sidebar = session.web.OldWidget.extend({ }); }); }, - do_fold: function() { - this.$element.addClass('closed-sidebar').removeClass('open-sidebar'); + do_attachement_update: function(dataset, model_id) { + if (!model_id) { + this.on_attachments_loaded([]); + } else { + var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ]; + var ds = new instance.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom); + ds.read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded); + } }, - do_unfold: function() { - this.$element.addClass('open-sidebar').removeClass('closed-sidebar'); + on_attachments_loaded: function(attachments) { + var self = this; + var items = []; + // TODO: preprend: _s + + var prefix = '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id='; + _.each(attachments,function(a) { + a.label = a.name; + if(a.type === "binary") { + a.url = prefix + a.id + '&t=' + (new Date().getTime()); + } + }); + attachments.push( { label: _t("Add..."), callback: self.on_attachment_add } ); + self.items['files'] = attachments; + self.redraw(); + }, + on_attachment_add: function(e) { + this.$element.find('.oe_sidebar_add').show(); + }, + on_attachment_changed: function(e) { + return; + window[this.element_id + '_iframe'] = this.do_update; + var $e = $(e.target); + if ($e.val() != '') { + this.$element.find('form.oe-binary-form').submit(); + $e.parent().find('input[type=file]').prop('disabled', true); + $e.parent().find('button').prop('disabled', true).find('img, span').toggle(); + } }, - do_toggle: function() { - this.$element.toggleClass('open-sidebar closed-sidebar'); + on_attachment_delete: function(e) { + return; + var self = this, $e = $(e.currentTarget); + var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text()); + if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) { + this.rpc('/web/dataset/unlink', { + model: 'ir.attachment', + ids: [parseInt($e.attr('data-id'))] + }, function(r) { + $e.parent().remove(); + self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted"); + }); + } } }); -session.web.TranslateDialog = session.web.Dialog.extend({ +instance.web.TranslateDialog = instance.web.Dialog.extend({ dialog_title: {toString: function () { return _t("Translations"); }}, init: function(view) { // TODO fme: should add the language to fields_view_get because between the fields view get @@ -979,14 +929,14 @@ session.web.TranslateDialog = session.web.Dialog.extend({ this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name }); this.languages = null; this.languages_loaded = $.Deferred(); - (new session.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), + (new instance.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded); }, start: function() { var self = this; this._super(); $.when(this.languages_loaded).then(function() { - self.$element.html(session.web.qweb.render('TranslateDialog', { widget: self })); + self.$element.html(instance.web.qweb.render('TranslateDialog', { widget: self })); self.$fields_form = self.$element.find('.oe_translation_form'); self.$fields_form.find('.oe_trad_field').change(function() { $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); @@ -1075,29 +1025,61 @@ session.web.TranslateDialog = session.web.Dialog.extend({ } }); -session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ +instance.web.View = instance.web.Widget.extend({ template: "EmptyComponent", // name displayed in view switchers display_name: '', + /** + * Define a view type for each view to allow automatic call to fields_view_get. + */ + view_type: undefined, init: function(parent, dataset, view_id, options) { this._super(parent); this.dataset = dataset; this.view_id = view_id; this.set_default_options(options); }, + start: function() { + return this.load_view(); + }, + load_view: function() { + if (this.embedded_view) { + var def = $.Deferred(); + var self = this; + $.async_when().then(function() {def.resolve(self.embedded_view);}); + return def.pipe(this.on_loaded); + } else { + var context = new instance.web.CompoundContext(this.dataset.get_context()); + if (! this.view_type) + console.warn("view_type is not defined", this); + return this.rpc("/web/view/load", { + "model": this.dataset.model, + "view_id": this.view_id, + "view_type": this.view_type, + toolbar: !!this.options.$sidebar, + context: context + }).pipe(this.on_loaded); + } + }, + /** + * Called after a successful call to fields_view_get. + * Must return a promise. + */ + on_loaded: function(fields_view_get) { + }, set_default_options: function(options) { this.options = options || {}; _.defaults(this.options, { // All possible views options should be defaulted here + $sidebar: null, sidebar_id: null, - sidebar: true, action: null, action_views_ids: {} }); }, open_translate_dialog: function(field) { if (!this.translate_dialog) { - this.translate_dialog = new session.web.TranslateDialog(this).start(); + this.translate_dialog = new instance.web.TranslateDialog(this).start(); } this.translate_dialog.open(field); }, @@ -1109,7 +1091,7 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``) * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'`` * @param {Object} [action_data.context=null] additional action context, to add to the current context - * @param {session.web.DataSet} dataset a dataset object used to communicate with the server + * @param {instance.web.DataSet} dataset a dataset object used to communicate with the server * @param {Object} [record_id] the identifier of the object on which the action is to be applied * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action) */ @@ -1121,12 +1103,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ return self.getParent().on_action_executed.apply(null, arguments); } }; - var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {}); + var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {}); var handler = function (r) { var action = r.result; if (action && action.constructor == Object) { - var ncontext = new session.web.CompoundContext(context); + var ncontext = new instance.web.CompoundContext(context); if (record_id) { ncontext.add({ active_id: record_id, @@ -1175,13 +1157,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ /** * Directly set a view to use instead of calling fields_view_get. This method must * be called before start(). When an embedded view is set, underlying implementations - * of session.web.View must use the provided view instead of any other one. + * of instance.web.View must use the provided view instead of any other one. * * @param embedded_view A view. */ set_embedded_view: function(embedded_view) { this.embedded_view = embedded_view; - this.options.sidebar = false; }, do_show: function () { this.$element.show(); @@ -1214,15 +1195,12 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ }, do_search: function(view) { }, - set_common_sidebar_sections: function(sidebar) { - sidebar.add_default_sections(); - }, on_sidebar_import: function() { - var import_view = new session.web.DataImport(this, this.dataset); + var import_view = new instance.web.DataImport(this, this.dataset); import_view.start(); }, on_sidebar_export: function() { - var export_view = new session.web.DataExport(this, this.dataset); + var export_view = new instance.web.DataExport(this, this.dataset); export_view.start(); }, on_sidebar_translate: function() { @@ -1247,7 +1225,29 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ } }); -session.web.json_node_to_xml = function(node, human_readable, indent) { +instance.web.xml_to_json = function(node) { + switch (node.nodeType) { + case 3: + case 4: + return node.data; + break; + case 1: + var attrs = $(node).getAttributes(); + _.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) { + if (attrs[key]) { + try { + attrs[key] = JSON.parse(attrs[key]); + } catch(e) { } + } + }); + return { + tag: node.tagName.toLowerCase(), + attrs: attrs, + children: _.map(node.childNodes, instance.web.xml_to_json) + } + } +} +instance.web.json_node_to_xml = function(node, human_readable, indent) { // For debugging purpose, this function will convert a json node back to xml // Maybe useful for xml view editor indent = indent || 0; @@ -1276,7 +1276,7 @@ session.web.json_node_to_xml = function(node, human_readable, indent) { r += '>' + cr; var childs = []; for (var i = 0, ii = node.children.length; i < ii; i++) { - childs.push(session.web.json_node_to_xml(node.children[i], human_readable, indent + 1)); + childs.push(instance.web.json_node_to_xml(node.children[i], human_readable, indent + 1)); } r += childs.join(cr); r += cr + sindent + '</' + node.tag + '>'; @@ -1285,6 +1285,43 @@ session.web.json_node_to_xml = function(node, human_readable, indent) { return r + '/>'; } } +instance.web.xml_to_str = function(node) { + if (window.ActiveXObject) { + return node.xml; + } else { + return (new XMLSerializer()).serializeToString(node); + } +} +instance.web.str_to_xml = function(s) { + if (window.DOMParser) { + var dp = new DOMParser(); + var r = dp.parseFromString(s, "text/xml"); + if (r.body && r.body.firstChild && r.body.firstChild.nodeName == 'parsererror') { + throw new Error("Could not parse string to xml"); + } + return r; + } + var xDoc; + try { + xDoc = new ActiveXObject("MSXML2.DOMDocument"); + } catch (e) { + throw new Error("Could not find a DOM Parser: " + e.message); + } + xDoc.async = false; + xDoc.preserveWhiteSpace = true; + xDoc.loadXML(s); + return xDoc; +} + +/** + * Registry for all the client actions key: tag value: widget + */ +instance.web.client_actions = new instance.web.Registry(); + +/** + * Registry for all the main views + */ +instance.web.views = new instance.web.Registry(); }; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 894f7353c1a..0e1edb89969 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -2,6 +2,14 @@ <!-- vim:fdl=1: --> <templates id="template" xml:space="preserve"> +<t t-name="EmptyComponent"> + <div></div> +</t> +<t t-name="Loading"> + <div class="oe_loading"> + Loading... + </div> +</t> <t t-name="Notification"> <div class="oe_notification" t-translation="off"> <div id="oe_notification_default"> @@ -17,208 +25,8 @@ </div> </div> </t> -<t t-name="WebClient"> - <div class="oe_webclient"> - <div class="oe_topbar"> - <div class="oe_menu_placeholder"/> - <div class="oe_user_menu_placeholder"/> - <div class="oe_systray"/> - </div> - <div class="oe_leftbar"> - <a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a> - <div class="oe_secondary_menus_container"/> - - <div class="oe_footer"> - Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a> - </div> - </div> - <div class="oe_application"/> - </div> -</t> -<t t-name="Loading"> - <div id="oe_loading"> - <div class="loading"> - Loading... - </div> - </div> -</t> -<t t-name="Database.CreateDB"> - <form name="create_db_form" class="oe_forms" method="POST"> - <table width="100%"> - <tr> - <td class="option_string"> - CREATE DATABASE - </td> - </tr> - </table> - <table align="center" class="db_option_table"> - <tr> - <td><label for="super_admin_pwd">Master password:</label></td> - <td><input type="password" name="super_admin_pwd" class="required" value="admin"/></td> - </tr> - <tr> - <td><label for="db_name">New database name:</label></td> - <td><input type="text" name="db_name" - class="required" matches="^[a-zA-Z][a-zA-Z0-9_]+$"/></td> - </tr> - <tr> - <td><label for="demo_data">Load Demonstration data:</label></td> - <td><input type="checkbox" name="demo_data"/></td> - </tr> - <tr> - <td><label for="db_lang">Default language:</label></td> - <td> - <select name="db_lang" t-if="lang_list"> - <t t-foreach="lang_list" t-as="lang"> - <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined"><t t-esc="lang[1]"/></option> - </t> - </select> - </td> - </tr> - <tr> - <td><label for="create_admin_pwd">Admin password:</label></td> - <td><input type="password" name="create_admin_pwd" class="required"/></td> - </tr> - <tr> - <td><label for="create_confirm_pwd">Confirm password:</label></td> - <td><input type="password" name="create_confirm_pwd" class="required" - equalTo="input[name=create_admin_pwd]"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Create</button></td> - </tr> - </table> - </form> -</t> -<t t-name="DropDB"> - <form name="drop_db_form" class="oe_forms" method="POST"> - <table width="100%"> - <tr> - <td class="option_string"> - DROP DATABASE - </td> - </tr> - </table> - <table align="center" class="db_option_table"> - <tr> - <td><label for="drop_db">Database:</label></td> - <td> - <select t-if="db_list" name="drop_db" autofocus="autofocus"> - <t t-foreach="db_list" t-as="db"> - <option t-att-value="db"><t t-esc="db"/></option> - </t> - </select> - <input t-if="!db_list" name="drop_db" class="required" - type="text" autofocus="autofocus"/> - </td> - </tr> - <tr> - <td><label for="drop_password">Master Password:</label></td> - <td><input type="password" name="drop_pwd" class="required"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Drop</button></td> - </tr> - </table> - </form> -</t> -<t t-name="BackupDB"> - <form name="backup_db_form" class="oe_forms" method="POST" target="backup-target" - action="/web/database/backup"> - <input type="hidden" name="token"/> - <table width="100%"> - <tr> - <td class="option_string"> - BACKUP DATABASE - </td> - </tr> - </table> - <table align="center" class="db_option_table"> - <tr> - <td><label for="backup_db">Database:</label></td> - <td> - <select t-if="db_list" name="backup_db" autofocus="autofocus"> - <t t-foreach="db_list" t-as="db"> - <option t-att-value="db"><t t-esc="db"/></option> - </t> - </select> - <input t-if="!db_list" name="backup_db" class="required" - type="text" autofocus="autofocus"/> - </td> - </tr> - <tr> - <td><label for="backup_pwd">Master Password:</label></td> - <td><input type="password" name="backup_pwd" class="required"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Backup</button></td> - </tr> - </table> - </form> -</t> -<t t-name="RestoreDB"> - <form name="restore_db_form" class="oe_forms" method="POST"> - <table width="100%"> - <tr> - <td class="option_string"> - RESTORE DATABASE - </td> - </tr> - </table> - <table align="center" class="db_option_table"> - <tr> - <td><label for="restore_db">File:</label></td> - <td><input type="file" name="db_file" class="required" - autofocus="autofocus"/></td> - </tr> - <tr> - <td><label for="restore_pwd">Master Password:</label></td> - <td><input type="password" name="restore_pwd" class="required"/></td> - </tr> - <tr> - <td><label for="new_db">New database name:</label></td> - <td><input type="text" name="new_db" class="required"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Restore</button></td> - </tr> - </table> - </form> -</t> -<t t-name="Change_DB_Pwd"> - <form name="change_pwd_form" class="oe_forms" method="POST"> - <table width="100%"> - <tr> - <td class="option_string"> - CHANGE MASTER PASSWORD - </td> - </tr> - </table> - <table align="center" class="db_option_table"> - <tr> - <td><label for="old_pwd">Master password:</label></td> - <td><input type="password" name="old_pwd" class="required" - minlength="1" autofocus="autofocus"/></td> - </tr> - <tr> - <td><label for="new_pwd">New master password:</label></td> - <td><input type="password" name="new_pwd" class="required" - minlength="1"/></td> - </tr> - <tr> - <td><label for="confirm_pwd">Confirm new master password:</label></td> - <td><input type="password" name="confirm_pwd" class="required" - equalTo="input[name=new_pwd]" minlength="1"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Change Password</button></td> - </tr> - </table> - </form> -</t> - -<t t-name="CrashManagerWarning"> +<t t-name="CrashManager.warning"> <table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning"> <tr> <td><img t-att-src='_s + "/web/static/src/img/warning.png"' class="oe-dialog-icon"/></td> @@ -235,7 +43,7 @@ </tr> </table> </t> -<t t-name="CrashManagerError"> +<t t-name="CrashManager.error"> <t t-if="!session.openerp_entreprise"> <span>Your version of OpenERP is unsupported. Support & maintenance services are available here: <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">OpenERP Entreprise</a>.</span> </t> @@ -266,7 +74,33 @@ </div> </t> -<t t-name="Login_dblist"> +<t t-name="Login"> + <div class="oe_login"> + <div class="oe_login_bottom"> </div> + <div class="oe_login_error_message">Invalid username or password</div> + <div class="oe_login_pane"> + <div class="oe_login_logo"><img src='/web/static/src/img/logo2.png'/></div> + <form action="" method="post"> + <div class="oe_login_dbpane" > + Database: + <input name="db" t-att-value="widget.selected_db || ''"/> + </div> + <ul> + <li>Username</li> + <li><input type="text" name="login" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li> + <li>Password</li> + <li><input type="password" name="password" t-att-value="widget.selected_password || ''"/></li> + <li><button name="submit">Log in</button></li> + </ul> + </form> + <div class="oe_login_footer"> + <a href="#" class="oe_login_manage_db">Manage Databases</a> | + <a href="http://www.openerp.com">Powered by <span>OpenERP</span></a> + </div> + </div> + </div> +</t> +<t t-name="Login.dblist"> <select name="db"> <t t-foreach="db_list" t-as="db"> <t t-if="selected_db === db"> @@ -280,51 +114,179 @@ </select> </t> -<t t-name="Login"> - <div class="login"> - <div class="bottom"> </div> - <div class="login_error_message">Invalid username or password</div> - <div class="pane"> - <div id="logo"><img src='/web/static/src/img/logo2.png'/></div> - <form action="" method="post"> - <div class="dbpane" > - Database: - <input name="db" t-att-value="widget.selected_db || ''"/> - </div> - <ul> - <li>Username</li> - <li><input type="text" name="login" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li> - <li>Password</li> - <li><input type="password" name="password" t-att-value="widget.selected_password || ''"/></li> - <li><button name="submit">Log in</button></li> - </ul> - </form> - <div class="footer"> - <a href="#" id="oe-db-config">Manage Databases</a> | - <a href="http://www.openerp.com">Powered by <span class="openerp">OpenERP</span></a> - </div> - </div> - </div> -</t> <t t-name="DatabaseManager"> - <div class="oe-database-manager"> - <div class="database"> - <a class="company_logo_link" href="/?"> - <div class="company_logo"></div> - </a> - <ul class="db_options"> - <li id="db-create">Create</li> - <li id="db-drop">Drop</li> - <li id="db-backup">Backup</li> - <li id="db-restore">Restore</li> - <li id="db-change-password">Password</li> - <li id="back-to-login">Back to Login</li> + <div class="oe_database_manager"> + <div class="oe_database_manager_menu"> + <ul class="oe_form_notebook"> + <li><a href="#db_create">Create</a></li> + <li><a href="#db_drop">Drop</a></li> + <li><a href="#db_backup">Backup</a></li> + <li><a href="#db_restore">Restore</a></li> + <li><a href="#db_change_password">Password</a></li> + <li><a id="back_to_login" href="#">Back to Login</a></li> </ul> + <form id="db_create" name="create_db_form" class="oe_forms" method="POST"> + <table align="center" class="db_option_table"> + <tr> + <th colspan="2" class="option_string"> CREATE DATABASE </th> + </tr> + <tr> + <td><label for="super_admin_pwd">Master password:</label></td> + <td><input type="password" name="super_admin_pwd" class="required" value="admin"/></td> + </tr> + <tr> + <td><label for="db_name">New database name:</label></td> + <td><input type="text" name="db_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_]+$"/></td> + </tr> + <tr> + <td><label for="demo_data">Load Demonstration data:</label></td> + <td><input type="checkbox" name="demo_data"/></td> + </tr> + <tr> + <td><label for="db_lang">Default language:</label></td> + <td> + <select name="db_lang" t-if="widget.lang_list"> + <t t-foreach="widget.lang_list" t-as="lang"> + <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined"><t t-esc="lang[1]"/></option> + </t> + </select> + </td> + </tr> + <tr> + <td><label for="create_admin_pwd">Admin password:</label></td> + <td><input type="password" name="create_admin_pwd" class="required"/></td> + </tr> + <tr> + <td><label for="create_confirm_pwd">Confirm password:</label></td> + <td><input type="password" name="create_confirm_pwd" class="required" equalTo="input[name=create_admin_pwd]"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Create</button></td> + </tr> + </table> + </form> + <form id="db_drop" name="drop_db_form" class="oe_forms" method="POST"> + <table align="center" class="db_option_table"> + <tr> + <th colspan="2" class="option_string"> DROP DATABASE </th> + </tr> + <tr> + <td><label for="drop_db">Database:</label></td> + <td> + <select t-if="widget.db_list" name="drop_db" autofocus="autofocus"> + <t t-foreach="widget.db_list" t-as="db"> + <option t-att-value="db"><t t-esc="db"/></option> + </t> + </select> + <input t-if="!widget.db_list" name="drop_db" class="required" type="text" autofocus="autofocus"/> + </td> + </tr> + <tr> + <td><label for="drop_password">Master Password:</label></td> + <td><input type="password" name="drop_pwd" class="required"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Drop</button></td> + </tr> + </table> + </form> + <form id="db_backup" name="backup_db_form" class="oe_forms" method="POST" target="backup-target" action="/web/database/backup"> + <input type="hidden" name="token"/> + <table align="center" class="db_option_table"> + <tr> + <th colspan="2" class="option_string"> BACKUP DATABASE </th> + </tr> + <tr> + <td><label for="backup_db">Database:</label></td> + <td> + <select t-if="widget.db_list" name="backup_db" autofocus="autofocus"> + <t t-foreach="widget.db_list" t-as="db"> + <option t-att-value="db"><t t-esc="db"/></option> + </t> + </select> + <input t-if="!widget.db_list" name="backup_db" class="required" type="text" autofocus="autofocus"/> + </td> + </tr> + <tr> + <td><label for="backup_pwd">Master Password:</label></td> + <td><input type="password" name="backup_pwd" class="required"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Backup</button></td> + </tr> + </table> + </form> + <form id="db_restore" name="restore_db_form" class="oe_forms" method="POST"> + <table align="center" class="db_option_table"> + <tr> + <th colspan="2" class="option_string"> RESTORE DATABASE </th> + </tr> + <tr> + <td><label for="restore_db">File:</label></td> + <td><input type="file" name="db_file" class="required" autofocus="autofocus"/></td> + </tr> + <tr> + <td><label for="restore_pwd">Master Password:</label></td> + <td><input type="password" name="restore_pwd" class="required"/></td> + </tr> + <tr> + <td><label for="new_db">New database name:</label></td> + <td><input type="text" name="new_db" class="required"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Restore</button></td> + </tr> + </table> + </form> + <form id="db_change_password" name="change_pwd_form" class="oe_forms" method="POST"> + <table align="center" class="db_option_table"> + <tr> + <th colspan="2" class="option_string"> CHANGE MASTER PASSWORD </th> + </tr> + <tr> + <td><label for="old_pwd">Master password:</label></td> + <td><input type="password" name="old_pwd" class="required" minlength="1" autofocus="autofocus"/></td> + </tr> + <tr> + <td><label for="new_pwd">New master password:</label></td> + <td><input type="password" name="new_pwd" class="required" minlength="1"/></td> + </tr> + <tr> + <td><label for="confirm_pwd">Confirm new master password:</label></td> + <td><input type="password" name="confirm_pwd" class="required" equalTo="input[name=new_pwd]" minlength="1"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Change Password</button></td> + </tr> + </table> + </form> </div> - <div id="oe_db_options" class="oe_db_options"></div> </div> </t> +<t t-name="WebClient"> + <table class="oe_webclient"> + <tr> + <td colspan="2" class="oe_topbar"> + <div class="oe_menu_placeholder"/> + <div class="oe_user_menu_placeholder"/> + <div class="oe_systray"/> + </td> + </tr> + <tr> + <td class="oe_leftbar" valign="top"> + <a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a> + <div class="oe_secondary_menus_container"/> + <div class="oe_footer"> + Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a> + </div> + </td> + <td class="oe_application"> + </td> + </tr> + </table> +</t> + <t t-name="Menu"> <ul class="oe_menu" t-if="widget.data"> <li t-foreach="widget.data.data.children" t-as="menu"> @@ -362,12 +324,17 @@ </ul> </t> <t t-name="Menu.secondary.link"> - <a href="#" + <a t-attf-href="#menu_id=#{menu.id}&action_id=#{menu.action ? menu.action.split(',')[1] : ''}" t-att-class="menu.children.length ? 'oe_menu_toggler' : 'oe_menu_leaf'" t-att-data-menu="menu.id" t-att-data-action-model="menu.action ? menu.action.split(',')[0] : ''" t-att-data-action-id="menu.action ? menu.action.split(',')[1] : ''"> <t t-esc="menu.name"/> + <t t-if="menu.needaction_enabled"> + <div class="oe_menu_counter"> + <t t-esc="menu.needaction_counter"/> + </div> + </t> </a> </t> @@ -396,73 +363,96 @@ </a> </li> </t> +<t t-name="About-Page"> + <div> + <a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developper mode</a> + <h1 style="margin:0;">OpenERP</h1> + <h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3> + <p> + Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br /> + OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>. + </p> + <p> + Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a> + </p> + <p> + For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a> + </p> + </div> +</t> +<t t-name="Change_Pwd"> + <form name="change_password_form" class="oe_forms" method="POST"> + <table align="center"> + <tr> + <td><label for="old_pwd">Old Password:</label></td> + <td><input type="password" name="old_pwd" + minlength="1" autofocus="autofocus"/></td> + </tr> + <tr> + <td><label for="new_password">New Password:</label></td> + <td><input type="password" name="new_password" + minlength="1" autofocus="autofocus"/></td> + </tr> + <tr> + <td><label for="confirm_pwd">Confirm Password:</label></td> + <td><input type="password" name="confirm_pwd" + minlength="1"/></td> + </tr> + <tr> + <td colspan="2" align="right"><button class="oe_button">Change Password</button></td> + </tr> + </table> + </form> +</t> <t t-name="ViewManager"> - <table class="view-manager-main-table" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td class="view-manager-main-content"> - <div class="oe-view-manager-header"> - <h2 class="oe_view_title" t-if="self.flags.display_title !== false"> - <span class="oe_view_title_text"><t t-esc="self.display_title()"/></span> - </h2> - <div class="oe_vm_switch"> - <t t-if="views.length != 1" t-foreach="views" t-as="view"> - - <button type="button" t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="view.label || view.view_type"> - <span><t t-esc="view.label || view.view_type"/></span> - </button> - </t> - </div> - </div> - <div t-attf-id="#{prefix}_search" t-opentag="true"/> - <t t-foreach="views" t-as="view"> - <div t-attf-id="#{prefix}_view_#{view.view_type}"/> - </t> - </td> - <td class="view-manager-main-sidebar" height="100%"> - <t t-foreach="views" t-as="view"> - <div t-attf-id="#{prefix}_sidebar_#{view.view_type}" class="sidebar-main-div closed-sidebar" style="display: none"/> + <div class="oe_view_manager"> + <table class="oe_view_manager_header2"> + <col width="33%"/> + <col width="33%"/> + <col width="34%"/> + <tr> + <td colspan="2"> + <h2 class="oe_view_title" t-if="widget.flags.display_title !== false"> + <span class="oe_view_title_text"><t t-esc="widget.display_title()"/></span> + </h2> + </td> + <td> <div class="oe_view_manager_view_search" t-opentag="true"/> </td> + </tr> + <tr> + <td> <div class="oe_view_manager_buttons"/> </td> + <td> <div class="oe_view_manager_sidebar"/> </td> + <td> + <ul class="oe_view_manager_switch oe_button_group oe_right"> + <t t-if="widget.views_src.length != 1" t-foreach="widget.views_src" t-as="view"> + <li><a t-attf-class="oe_vm_switch_#{view.view_type} oe_i" t-att-data-view-type="view.view_type" t-att-title="view.label || view.view_type"> + </a></li> + </t> + </ul> + <div class="oe_view_manager_pager oe_right"/> + </td> + </tr> + </table> + <div class="oe_view_manager_body"> + <t t-foreach="widget.views_src" t-as="view"> + <div t-attf-class="oe_view_manager_view_#{view.view_type}"/> </t> - </td> - </tr> - </tbody> - </table> + </div> + </div> </t> - -<t t-extend="ViewManager" t-name="ViewManagerAction"> - <t t-jquery=".view-manager-main-table tbody" t-operation="prepend"> - <tr> - <td class="oe_view_manager_menu_tips" colspan="2"> - <blockquote t-if="self.action.help and !self.flags.low_profile - and !(self.action.id in self.session.hidden_menutips)"> - <p><t t-esc="self.action.help"/></p> - <div> - <button type="button" name="hide">Hide this tip</button> - <button type="button" name="disable">Disable all tips</button> - </div> - </blockquote> - </td> - </tr> - </t> +<t t-name="ViewManagerAction" t-extend="ViewManager"> <t t-jquery="h2.oe_view_title" t-operation="prepend"> - <a t-if="self.flags.display_title !== false" class="oe-shortcut-toggle" title="Add / Remove Shortcut..." + <a t-if="widget.flags.display_title !== false" class="oe-shortcut-toggle" title="Add / Remove Shortcut..." href="javascript: void(0)"> </a> </t> <t t-jquery="h2.oe_view_title" t-operation="after"> - <select t-if="self.session.debug" class="oe_debug_view"/> - </t> - <t t-jquery=".oe-view-manager-header" t-operation="after"> - <div class="oe-view-manager-logs oe-folded"> - <ul></ul> - <a class="oe-more-logs" href="#">More…</a> - <a class="oe-remove-everything ui-icon ui-icon-closethick"/> - </div> + <select t-if="widget.session.debug" class="oe_debug_view"/> </t> </t> <t t-name="ViewManagerDebug"> <option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option> - <option t-if="_.indexOf(['form', 'page'], view_manager.active_view) gt -1" value="perm_read" data-views="form,page">View Log (perm_read)</option> + <option t-if="view_manager.active_view === 'form'" value="perm_read" data-views="form,page">View Log (perm_read)</option> + <option t-if="view_manager.active_view === 'form'" value="toggle_layout_outline">Toggle Form Layout Outline</option> <option value="fields">View Fields</option> <option value="fvg">Fields View Get</option> <t t-if="view_manager.session.uid === 1"> @@ -494,55 +484,84 @@ <span><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></span> </div> </t> -<t t-extend="ViewManager" t-name="One2Many.viewmanager"> - <t t-jquery="span.oe_view_title_text" t-operation="replace"/> - - <t t-jquery=".oe-view-manager-header"> - this.attr('t-if', 'views.length != 1'); - </t> +<t t-name="ViewPager"> + <div class="oe_pager_value"> + <t t-raw="__content__"/> + </div> + <ul class="oe_pager_group"> + <!-- + <button class="oe_button oe_button_pager" type="button" data-pager-action="first"> + <img t-att-src='_s + "/web/static/src/img/pager_first.png"'/> + </button> + --> + <li> + <a class="oe_i" type="button" data-pager-action="previous">(</a> + </li> + <li> + <a class="oe_i" type="button" data-pager-action="next">)</a> + </li> + <!-- + <button class="oe_button oe_button_pager" type="button" data-pager-action="last"> + <img t-att-src='_s + "/web/static/src/img/pager_last.png"'/> + </button> + --> + </ul> </t> <t t-name="Sidebar"> - <a class="toggle-sidebar"></a> - <div class="sidebar-content"> - <div class="sidebar-actions"> - </div> + <div class="oe_sidebar"> + <t t-foreach="widget.sections" t-as="section"> + <div class="oe_form_dropdown_section"> + <button class="oe_dropdown_toggle"><t t-esc="section.label"/></button> + <ul class="oe_dropdown_menu"> + <li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname"> + <a class="oe_sidebar_action_a" t-att-title="item.title" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url" target="_blank"> + <t t-raw="item.label"/> + </a> + </li> + <li t-if="section.name == 'files'" class="oe_sidebar_add" style="display:none;"> + Input type file stuff + </li> + </ul> + </div> + </t> </div> </t> -<t t-name="Sidebar.section"> - <div t-att-id="section_id" t-att-class="classname"> - <h2><t t-esc="name"/></h2> +<t t-name="Sidebar.attachments"> + <div class="oe-sidebar-attachments-toolbar"> + <div class="oe-binary-file-set" style="float: right"> + <form class="oe-binary-form" t-attf-target="#{element_id}_iframe" + method="post" enctype="multipart/form-data" action="/web/binary/upload_attachment"> + <input type="hidden" name="session_id" t-att-value="session.session_id"/> + <input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/> + <input type="hidden" name="model" t-att-value="view.dataset.model"/> + <input type="hidden" name="id" t-att-value="view.datarecord.id"/> + <button class="oe_button" type="button"> + <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/> + <span>Add</span> + </button> + <input type="file" class="oe-binary-file" name="ufile" title="Add attachment" + t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/> + </form> + <iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"/> + </div> </div> -</t> -<t t-name="Sidebar.section.items"> - <li t-foreach="items" t-as="item" t-att-class="item.classname"> - <a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#"> - <t t-esc="item.label"/> - </a> - </li> + <br style="clear: both"/> + <ul class="oe-sidebar-attachments-items"> + <li t-foreach="attachments" t-as="attachment"> + <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id=' + + session.session_id + '&model=ir.attachment&id=' + attachment.id + + '&field=datas&filename_field=name&t=' + (new Date().getTime())"/> + <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank"> + <t t-esc="attachment.name"/> + </a> + <a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}"> + <img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/> + </a> + </li> + </ul> </t> -<t t-name="TranslateDialog"> - <table t-if="widget.view.translatable_fields" class="oe_frame oe_forms oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%"> - <tr> - <td class="oe_form_separator" width="1%" nowrap="nowrap"> - <div class="separator horizontal">Field</div> - </td> - <th t-foreach="widget.languages" align="left"> - <div class="separator horizontal"><t t-esc="name"/></div> - </th> - </tr> - <tr t-foreach="widget.view.translatable_fields" t-as="field" t-att-data-field="field.name"> - <td class="oe_form_frame_cell" width="1%" nowrap="nowrap"> - <label class="oe_label"><t t-esc="field.string"/>:</label> - </td> - <td t-foreach="widget.languages" t-as="lg" class="oe_form_frame_cell"> - <input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" data-value="" class="oe_trad_field" style="width: 100%"/> - <textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" data-value="" class="oe_trad_field" style="width: 100%"></textarea> - </td> - </tr> - </table> -</t> <t t-name="TreeView"> <select t-if="toolbar" style="width: 30%"> </select> @@ -585,44 +604,14 @@ </td> </tr> -<t t-name="ViewPager"> - <button class="oe_button oe_button_pager" type="button" data-pager-action="first"> - <img t-att-src='_s + "/web/static/src/img/pager_first.png"'/> - </button> - <button class="oe_button oe_button_pager" type="button" data-pager-action="previous"> - <img t-att-src='_s + "/web/static/src/img/pager_previous.png"'/> - </button> - - <t t-raw="__content__"/> - - <button class="oe_button oe_button_pager" type="button" data-pager-action="next"> - <img t-att-src='_s + "/web/static/src/img/pager_next.png"'/> - </button> - <button class="oe_button oe_button_pager" type="button" data-pager-action="last"> - <img t-att-src='_s + "/web/static/src/img/pager_last.png"'/> - </button> -</t> - <table t-name="ListView" class="oe-listview-content"> <t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0) + (options.isClarkGable ? 1 : 0)"/> - <thead class="ui-widget-header"> + <thead> <tr t-if="options.action_buttons !== false or options.pager !== false"> <th t-att-colspan="columns_count"> - <table> - <tr> - <td t-if="!no_leaf and options.action_buttons !== false" class="oe-actions"> - <button type="button" class="oe_button oe-list-add" - t-if="options.addable"> - <t t-esc="options.addable"/> - </button> - <button type="button" class="oe_button oe-list-delete" - t-if="options.selectable and options.deletable"> - Delete - </button> - </td> - <t t-call="Listview.navigation.button"/> - </tr> - </table> + <div class="oe_list_buttons"/> + <div class="oe_list_sidebar"/> + <div class="oe_list_pager"/> </th> </tr> <tr t-if="options.header" class="oe-listview-header-columns"> @@ -632,19 +621,19 @@ </th> </t> <th t-if="options.selectable" width="1" > - <input type="checkbox" class="all-record-selector"/> </th> - <th t-if="options.isClarkGable" width="1"> </th> + <input type="checkbox" class="all-record-selector"/> + </th> + <th t-if="options.isClarkGable" width="1"></th> <t t-foreach="columns" t-as="column"> <th t-if="!column.meta and column.invisible !== '1'" t-att-data-id="column.id" t-att-class="((options.sortable and column.tag !== 'button') ? 'oe-sortable' : null)"> - <t t-if="column.tag !== 'button'" - ><t t-esc="column.string"/></t> + <t t-if="column.tag !== 'button'"><t t-esc="column.string"/></t> </th> </t> <th t-if="options.deletable" width="1"/> </tr> </thead> - <tfoot class="ui-widget-header"> + <tfoot> <tr> <td t-if="options.selectable"/> <td t-if="options.isClarkGable"/> @@ -653,32 +642,26 @@ </td> <td t-if="options.deletable"/> </tr> - <tr> - <t t-call="Listview.navigation.button"/> - </tr> </tfoot> </table> -<t t-extend="ListView" t-name="One2Many.listview"> - <t t-jquery="thead.ui-widget-header > tr:first"> - this.removeAttr('t-if'); - </t> - - <t t-jquery="tfoot > tr:last-child" t-operation="replace"/> - - <t t-jquery="td.oe-actions"> - this.removeAttr('t-if'); - </t> - <t t-jquery="td.oe-actions" t-operation="prepend"> - <h3 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h3> +<div t-name="ListView.buttons" class="oe_list_buttons"> + <t t-if="!widget.no_leaf and widget.options.action_buttons !== false"> + <button type="button" class="oe_button oe_list_add" t-if="widget.options.addable"> + <t t-esc="widget.options.addable"/> + </button> + <button type="button" class="oe_button oe_list_delete" t-if="widget.options.selectable and widget.options.deletable"> + Delete + </button> </t> +</div> +<t t-name="ListView.pager"> + <div class="oe_list_pager" t-att-colspan="widget.columns_count"> + <t t-if="!widget.no_leaf and widget.options.pager !== false" t-call="ViewPager"> + <span class="oe-pager-state"> + </span> + </t> + </div> </t> -<th t-name="Listview.navigation.button" t-if="!no_leaf and options.pager !== false" - class="oe-list-pager" t-att-colspan="columns_count"> - <t t-call="ViewPager"> - <span class="oe-pager-state"> - </span> - </t> -</th> <t t-name="ListView.rows" t-foreach="records.length" t-as="index"> <t t-call="ListView.row"> <t t-set="record" t-value="records.at(index)"/> @@ -711,73 +694,38 @@ </td> </t> <td t-if="options.deletable" class='oe-record-delete' width="1"> - <button type="button" name="delete"></button> + <button type="button" name="delete" class="oe_i">d</button> </td> </tr> -<t t-name="ListView.row.form"> - <th t-if="widget.editable_list.options.selectable"></th> - <th t-if="widget.editable_list.options.isClarkGable"></th> -</t> <t t-name="FormView"> - <div class="oe_form_header"> - <div class="oe_form_buttons" t-if="widget.options.action_buttons !== false"> - <button type="button" class="oe_button oe_form_button_save">Save</button> - <button type="button" class="oe_button oe_form_button_cancel">Cancel</button> - </div> - <div class="oe_form_pager" t-if="widget.options.pager !== false"> - <t t-call="ViewPager"> - <span class="oe_pager_index">0</span><span class="oe_pager_separator"> / </span><span class="oe_pager_count">0</span> - </t> - </div> + <div class="oe_formview"> + <div class="oe_form_buttons"/> + <div class="oe_form_sidebar"/> + <div class="oe_form_pager"/> + <div class="oe_form_container"/> </div> </t> -<t t-name="One2Many.formview" t-extend="FormView"> - <t t-jquery=".oe_form_buttons" t-operation="inner"> - <button type="button" class="oe_button oe_form_button_create">Add</button> - </t> -</t> -<t t-name="PageView" t-extend="FormView"> - <t t-jquery=".oe_form_buttons" t-operation="inner"> - <button type="button" class="oe_button oe_form_button_edit">Edit</button> - <button type="button" class="oe_button oe_form_button_create">Create</button> - <button type="button" class="oe_button oe_form_button_duplicate">Duplicate</button> - <button type="button" class="oe_button oe_form_button_delete">Delete</button> +<div t-name="FormView.buttons" class="oe_form_buttons"> + <t t-if="widget.options.action_buttons !== false"> + <span class="oe_form_buttons_view"> + <button type="button" class="oe_button oe_form_button_edit">Edit</button> + <button type="button" class="oe_button oe_form_button_create">Create</button> + <button type="button" class="oe_button oe_form_button_duplicate">Duplicate</button> + <button type="button" class="oe_button oe_form_button_delete">Delete</button> + </span> + <span class="oe_form_buttons_edit"> + <button type="button" class="oe_button oe_form_button_save">Save</button> + <button type="button" class="oe_button oe_form_button_cancel">Cancel</button> + </span> </t> -</t> -<t t-name="FormView.sidebar.attachments"> - <div class="oe-sidebar-attachments-toolbar"> - <div class="oe-binary-file-set" style="float: right"> - <form class="oe-binary-form" t-attf-target="#{element_id}_iframe" - method="post" enctype="multipart/form-data" action="/web/binary/upload_attachment"> - <input type="hidden" name="session_id" t-att-value="session.session_id"/> - <input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/> - <input type="hidden" name="model" t-att-value="view.dataset.model"/> - <input type="hidden" name="id" t-att-value="view.datarecord.id"/> - <button class="oe_button" type="button"> - <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/> - <span>Add</span> - </button> - <input type="file" class="oe-binary-file" name="ufile" title="Add attachment" - t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/> - </form> - <iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"/> - </div> +</div> +<t t-name="FormView.pager"> + <div class="oe_form_pager"> + <t t-if="widget.options.pager !== false" t-call="ViewPager"> + <span class="oe_pager_index">0</span><span class="oe_pager_separator"> / </span><span class="oe_pager_count">0</span> + </t> </div> - <br style="clear: both"/> - <ul class="oe-sidebar-attachments-items"> - <li t-foreach="attachments" t-as="attachment"> - <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id=' - + session.session_id + '&model=ir.attachment&id=' + attachment.id - + '&field=datas&filename_field=name&t=' + (new Date().getTime())"/> - <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank"> - <t t-esc="attachment.name"/> - </a> - <a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}"> - <img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/> - </a> - </li> - </ul> </t> <form t-name="FormView.set_default" class="oe_forms oe_frame"> <t t-set="args" t-value="widget.dialog_options.args"/> @@ -789,7 +737,7 @@ Default: </label> </td> - <td class="required"> + <td class="oe_form_required"> <select id="formview_default_fields"> <option value=""/> <option t-foreach="args.fields" t-as="field" @@ -835,86 +783,69 @@ </tr> </table> </form> -<t t-name="Widget"> - Unhandled widget - <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t> -</t> -<t t-name="WidgetFrame"> - <table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms"> - <tr t-foreach="widget.table" t-as="row"> - <t t-foreach="row" t-as="td"> - <td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined" - t-att-width="td.width" - t-att-valign="td.table ? 'top' : undefined" - t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}" - > - <t t-raw="td.render()"/> - </td> - </t> - </tr> - </table> + +<t t-name="FormRenderingForm.manual"> + <div class="oe_form_manual_layout"/> </t> -<t t-name="WidgetFrame.readonly" t-extend="WidgetFrame"> - <t t-jquery=">table"> - this.attr('class', this.attr('class')+' oe_form_readonly'); - </t> +<t t-name="FormRenderingForm"> + <div class="oe_form_sheetbg"> + <div t-attf-class="oe_form_sheet oe_form_sheet_width #{classnames}"> + <group col="4"/> + </div> + </div> </t> -<t t-name="WidgetGroup"> - <t t-if="widget.string"> - <fieldset class="oe_group_box"> - <legend><t t-esc="widget.string"/></legend> - <t t-call="WidgetFrame"/> - </fieldset> +<t t-name="FormRenderingGroup"> + <t t-set="table"> + <table border="0" cellpadding="0" cellspacing="0" width="100%" t-att-class="classnames"/> </t> - <t t-if="!widget.string"> - <t t-call="WidgetFrame"/> + <t t-if="string"> + <fieldset class="oe_group_box"> + <legend><t t-esc="string"/></legend> + <t t-raw="table"/> + </fieldset> </t> -</t> -<t t-name="WidgetNotebook"> - <ul> - <li t-foreach="widget.pages" t-as="page"> - <a href="#"> - <t t-esc="page.string"/> - </a> - </li> - </ul> - <t t-foreach="widget.pages" t-as="page"> - <div t-att-class="page.element_class"> - </div> + <t t-if="!string"> + <t t-raw="table"/> </t> </t> -<t t-name="WidgetNotebook.tooltip"> - <t t-foreach="widget.pages" t-as="page"> - <div class="oe_tooltip_string"> - Notebook Page "<t t-esc="page.string"/>" - </div> - <ul class="oe_tooltip_technical"> - <li data-item="modifiers"> - <span class="oe_tooltip_technical_title">Modifiers:</span> - <t t-esc="page.node.attrs.modifiers"/> +<t t-name="FormRenderingNotebook"> + <div> + <ul t-attf-class="oe_form_notebook #{classnames}"> + <li t-foreach="pages" t-as="page" t-att-modifiers="page.modifiers"> + <a t-attf-href="##{page.id}"> + <t t-esc="page.string"/> + </a> </li> </ul> - </t> + </div> </t> -<t t-name="WidgetNotebookPage"> - <t t-call="WidgetFrame"/> +<t t-name="FormRenderingNotebookPage"> + <div t-attf-class="oe_form_notebook_page #{classnames}" t-att-id="id"> + <group t-if="layout === 'auto'" col="4"/> + </div> </t> -<t t-name="WidgetSeparator"> - <div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation"> - <t t-esc="widget.string"/> +<t t-name="FormRenderingSeparator"> + <div t-attf-class="oe_horizontal_separator #{classnames}"> + <t t-esc="string"/> </div> </t> -<t t-name="WidgetLabel"> - <label t-att-for="widget.element_id" - t-attf-class="oe_label#{widget.help ? '_help' : ''} oe_align_#{widget.align}"> - <t t-esc="widget.string"/> - <span t-if="widget.help">?</span> - <t t-if="widget.string and widget.node.tag != 'label'">:</t> +<t t-name="FormRenderingLabel"> + <label t-att-for="_for" + t-att-title="help" + t-attf-class="#{classnames} oe_form_label#{help ? '_help' : ''} oe_align_#{align}"> + <t t-esc="string"/> + <span t-if="help">?</span> + : </label> </t> + +<t t-name="Widget"> + Unhandled widget + <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t> +</t> <t t-name="WidgetLabel.tooltip"> <div class="oe_tooltip_string" t-if="widget.string"> - <t t-esc="widget.string"/> <t t-if="debug and widget.nolabel">(nolabel)</t> + <t t-esc="widget.node.attrs.string"/> <t t-if="debug and widget.nolabel">(nolabel)</t> </div> <p t-if="widget.help" class="oe_tooltip_help"><t t-esc="widget.help"/></p> <ul t-if="debug" class="oe_tooltip_technical"> @@ -974,68 +905,88 @@ </li> </ul> </t> -<t t-name="WidgetParagraph"> - <p t-attf-class="oe_form_paragraph oe_align_#{widget.align} #{widget.multilines ? 'oe_multilines' : ''}"><t t-esc="widget.string"/></p> -</t> <t t-name="FieldChar"> - <input t-att-type="widget.password ? 'password' : 'text'" size="1" - t-att-name="widget.name" - t-att-id="widget.element_id" - t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}" - style="width: 100%" - /><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> -</t> -<t t-name="FieldChar.readonly"> - <div - t-att-id="widget.element_id" - t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}"> - </div> -</t> -<t t-name="FieldURI.readonly"> - <div> - <a href="#" class="oe_form_uri"/> - </div> + <t t-if="!widget.get('effective_readonly')"> + <div class="oe_form_field_char"> + <input t-att-type="widget.password ? 'password' : 'text'" + t-att-id="widget.id_for_label" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + t-attf-class="field_char #{widget.is_field_number ? 'oe-number' : ''}" + /><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> + </div> + </t> + <t t-if="widget.get('effective_readonly')"> + <div t-attf-class="oe_form_field_char #{widget.is_field_number ? 'oe-number' : ''}"> + </div> + </t> </t> <t t-name="FieldEmail"> - <table cellpadding="0" cellspacing="0" border="0" width="100%"> - <tr> - <td width="100%"> - <t t-call="FieldChar"/> - </td> - <td width="16"> - <button class="oe_button oe_field_button" type="button" title="Send an e-mail with your default e-mail client"> - <img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/> - </button> - </td> - </tr> - </table> + <t t-if="!widget.get('effective_readonly')"> + <table class="oe_form_field_email" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td> + <input type="text" + t-att-id="widget.id_for_label" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + /> + </td> + <td width="16"> + <button class="oe_button oe_field_button" tabindex="-1" type="button" title="Send an e-mail with your default e-mail client"> + <img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/> + </button> + </td> + </tr> + </table> + </t> + <t t-if="widget.get('effective_readonly')"> + <div> + <a href="#" class="oe_form_uri"/> + </div> + </t> </t> <t t-name="FieldUrl"> - <table cellpadding="0" cellspacing="0" border="0" width="100%"> - <tr> - <td width="100%"> - <t t-call="FieldChar"/> - </td> - <td width="16"> - <button class="oe_button oe_field_button" type="button" title="Open this resource"> - <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/> - </button> - </td> - </tr> - </table> + <t t-if="!widget.get('effective_readonly')"> + <table class="oe_form_field_url" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td> + <input type="text" + t-att-id="widget.id_for_label" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + /> + </td> + <td width="16"> + <button class="oe_button oe_field_button" tabindex="-1" type="button" title="Open this resource"> + <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/> + </button> + </td> + </tr> + </table> + </t> + <t t-if="widget.get('effective_readonly')"> + <div> + <a href="#" class="oe_form_uri"/> + </div> + </t> </t> <t t-name="FieldText"> - <textarea rows="6" - t-att-name="widget.name" - t-att-id="widget.element_id" - t-attf-class="field_#{widget.type}" - style="width: 100%" - ></textarea><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> + <div class="oe_form_field_text"> + <textarea rows="6" + t-att-name="widget.name" + class="field_text" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + ></textarea> + <img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" + t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> + </div> </t> <t t-name="web.datetimepicker"> - <div class="oe_datepicker_root"> - <input type="text" size="1" class="oe_datepicker_container" disabled="disabled" style="display: none;"/> - <input type="text" size="1" style="width: 100%" + <div class="oe_datepicker_root oe_form_field_datetime"> + <input type="text" class="oe_datepicker_container" disabled="disabled" style="display: none;"/> + <input type="text" t-att-name="widget.name" t-attf-class="oe_datepicker_master field_#{widget.type_of_date}" /><img class="oe_input_icon oe_datepicker_trigger" t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"' @@ -1043,86 +994,107 @@ </div> </t> <t t-name="FieldSelection"> - <select - t-att-name="widget.name" - t-att-id="widget.element_id" - t-attf-class="field_#{widget.type}" - style="width: 100%"> - <t t-foreach="widget.values" t-as="option"> - <option><t t-esc="option[1]"/></option> - </t> - </select> + <t t-if="!widget.get('effective_readonly')"> + <div class="oe_form_field_selection"> + <select + t-att-name="widget.name" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + t-att-id="widget.id_for_label"> + <t t-foreach="widget.values" t-as="option"> + <option><t t-esc="option[1]"/></option> + </t> + </select> + </div> + </t> + <t t-if="widget.get('effective_readonly')"> + <div class="field_selection"> + </div> + </t> </t> <t t-name="FieldMany2One"> - <table cellpadding="0" cellspacing="0" border="0" width="100%"> - <tr> - <td width="100%" valign="top"> - <t t-call="FieldChar"/> - <span class="oe-m2o-drop-down-button"> - <img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span> - </td> - <td> - <button class="oe_button oe_field_button oe-m2o-cm-button" t-att-id="widget.name + '_open'"> - <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY_MENU.png"'/> - </button> - <div t-att-id="widget.cm_id" class="contextMenu" style="display:none"> - </div> - </td> - </tr> - </table> -</t> -<t t-name="FieldMany2One.context_menu"> - <ul> - <li t-att-id="widget.cm_id + '_open'" style="color:grey" class="oe_m2o_menu_item_mandatory">Open...</li> - <li t-att-id="widget.cm_id + '_create'" class="oe_m2o_menu_item_noreadonly">Create...</li> - <li t-att-id="widget.cm_id + '_search'" class="oe_m2o_menu_item_noreadonly">Search...</li> - <t t-set="i" t-value="0"/> - <t t-foreach="widget.related_entries" t-as="entry"> - <li t-att-id="widget.cm_id + '_related_' + i" style="color:grey" class="oe_m2o_menu_item_mandatory"> - ... <t t-esc="(entry[2] || {})['name'] || ''"/> - </li> - <t t-set="i" t-value="i+1"/> - </t> - </ul> -</t> -<t t-name="FieldOne2Many"> -</t> -<t t-name="FieldMany2Many"> - <div t-att-id="widget.list_id"></div> + <t t-if="!widget.get('effective_readonly')"> + <table class="oe_form_field_many2one" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td valign="top"> + <input type="text" + t-att-id="widget.id_for_label" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + /> + <span class="oe-m2o-drop-down-button"> + <img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span> + </td> + <td width="16"> + <button class="oe_button oe_field_button oe-m2o-cm-button"> + <img t-att-src='_s + "/web/static/src/img/icons/gtk-go-forward.png"'/> + </button> + </td> + </tr> + </table> + </t> + <t t-if="widget.get('effective_readonly')"> + <div> + <a href="#" class="oe_form_uri"/> + </div> + </t> </t> <t t-name="FieldReference"> - <table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms"> - <tr> - <td t-attf-class="oe_form_frame_cell oe_form_selection oe_form_view_reference_selection"> - </td> - <td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none"> - </td> - </tr> - </table> + <t t-if="!widget.get('effective_readonly')"> + <table class="oe_form_field_reference oe_frame oe_forms" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td t-attf-class="oe_form_frame_cell oe_form_selection oe_form_view_reference_selection"> + </td> + <td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none"> + </td> + </tr> + </table> + </t> + <t t-if="widget.get('effective_readonly')"> + <a href="#" class="oe_form_uri"/> + </t> </t> <t t-name="FieldBoolean"> - <input type="checkbox" - t-att-name="widget.name" - t-att-id="widget.element_id" - t-attf-class="field_#{widget.type}"/> + <span class="oe_form_field_boolean"> + <input type="checkbox" + t-att-id="widget.id_for_label" + t-att-name="widget.name" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + class="field_boolean"/> + </span> </t> <t t-name="FieldProgressBar"> - <div t-opentag="true" class="oe-progressbar"> + <div class="oe_form_field_progressbar" t-opentag="true"> <span></span> </div> </t> +<t t-name="FieldStatus.content"> + <ul class="oe-arrow-list"> + <t t-set="size" t-value="widget.to_show.length"/> + <t t-foreach="_.range(size)" t-as="i"> + <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe-arrow-list-selected' : ''"> + <span class="oe-arrow-list-before" t-if="i > 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i < size - 1"></span> + </li> + </t> + </ul> +</t> +<t t-name="FieldStatus.content"> + <ul class="oe_form_steps"> + <t t-set="size" t-value="widget.to_show.length"/> + <t t-foreach="_.range(size)" t-as="i"> + <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe_form_steps_active' : ''"> + <t t-esc="widget.to_show[i][1]"/> + <img t-att-src='_s + "/web/static/src/img/form_steps.png"' class="oe_form_steps_arrow" t-if="i < size - 1"/> + </li> + </t> + </ul> +</t> <t t-name="FieldBinaryImage"> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <tr> - <td align="center"> - <img t-att-src='_s + "/web/static/src/img/placeholder.png"' class="oe-binary-image" - t-att-border="widget.readonly ? 0 : 1" - t-att-id="widget.element_id + '_field'" - t-att-name="widget.name" - t-attf-class="field_#{widget.type}" - t-att-width="widget.node.attrs.img_width || widget.node.attrs.width" - t-att-height="widget.node.attrs.img_height || widget.node.attrs.height" - /> + <td class="oe_form_field-binary-image-placeholder" align="center"> + </td> </tr> <tr> @@ -1131,7 +1103,7 @@ <table cellspacing="0" cellpadding="0" border="0"> <tr> <td> - <div class="oe-binary-file-set" style="width: 40px; height:22px;"> + <div class="oe-binary-file-set"> <form class="oe-binary-form" t-att-target="widget.iframe" method="post" enctype="multipart/form-data" action="/web/binary/upload"> <input type="hidden" name="session_id" value=""/> @@ -1139,7 +1111,10 @@ <button class="oe_button" type="button" title="Set Image"> <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/> </button> - <input type="file" class="oe-binary-file" name="ufile"/> + <input type="file" class="oe-binary-file" name="ufile" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + /> </form> </div> </td> @@ -1160,58 +1135,77 @@ </tr> </table> </t> +<t t-name="FieldBinaryImage-img"> + <img t-att-src='url' class="oe-binary-image field_image" + t-att-border="widget.readonly ? 0 : 1" + t-att-name="widget.name" + t-att-width="widget.node.attrs.img_width || widget.node.attrs.width" + t-att-height="widget.node.attrs.img_height || widget.node.attrs.height" + t-att-style="'max-width: ' + (widget.$element.width() > 60 ? widget.$element.width() : 60) + 'px'" + /> +</t> <t t-name="FieldBinaryFile"> - <table cellpadding="0" cellspacing="0" border="0" width="100%"> - <tr> - <td width="100%"> - <input type="text" size="1" readonly="readonly" - t-att-name="widget.name" - t-att-id="widget.element_id + '_field'" - t-attf-class="field_#{widget.type}" style="width: 100%" - /> - </td> - <td class="oe-binary" nowrap="true"> - <table cellspacing="0" cellpadding="0" border="0"> - <tr> - <td> - <div class="oe-binary-file-set" style="width: 80px; height:22px;"> - <form class="oe-binary-form" t-att-target="widget.iframe" - method="post" enctype="multipart/form-data" action="/web/binary/upload"> - <input type="hidden" name="session_id" value=""/> - <input type="hidden" name="callback" t-att-value="widget.iframe"/> - <button class="oe_button oe_field_button" type="button" title="Set Image"> - <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/> - <span>Select</span> - </button> - <input type="file" class="oe-binary-file" name="ufile"/> - </form> - </div> - </td> - <td> - <button class="oe_button oe-binary-file-save" type="button" title="Save As"> - <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/> - <span>Save As</span> - </button> - </td> - <td> - <button class="oe_button oe-binary-file-clear" type="button" title="Clear"> - <img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/> - <span>Clear</span> - </button> - </td> - </tr> - </table> - </td> - <td class="oe-binary-progress" style="display: none" nowrap="true"> - <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/> - <b>Uploading ...</b> - <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/> - </td> - </tr> - </table> + <t t-if="!widget.get('effective_readonly')"> + <table class="oe_form_field_binary" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td> + <input type="text" size="1" readonly="readonly" + t-att-name="widget.name" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus" + class="field_binary" + /> + </td> + <td class="oe-binary" nowrap="true"> + <table cellspacing="0" cellpadding="0" border="0"> + <tr> + <td> + <div class="oe-binary-file-set" style="width: 80px; height:22px;"> + <form class="oe-binary-form" t-att-target="widget.iframe" + method="post" enctype="multipart/form-data" action="/web/binary/upload"> + <input type="hidden" name="session_id" value=""/> + <input type="hidden" name="callback" t-att-value="widget.iframe"/> + <button class="oe_button oe_field_button" type="button" title="Set Image"> + <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/> + <span>Select</span> + </button> + <input type="file" class="oe-binary-file" name="ufile"/> + </form> + </div> + </td> + <td> + <button class="oe_button oe-binary-file-save" type="button" title="Save As"> + <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/> + <span>Save As</span> + </button> + </td> + <td> + <button class="oe_button oe-binary-file-clear" type="button" title="Clear"> + <img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/> + <span>Clear</span> + </button> + </td> + </tr> + </table> + </td> + <td class="oe-binary-progress" style="display: none" nowrap="true"> + <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/> + <b>Uploading ...</b> + <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/> + </td> + </tr> + </table> + </t> + <t t-if="widget.get('effective_readonly')"> + <div> + <a href="javascript:void(0)" class="oe_form_uri"/> + </div> + </t> </t> <t t-name="WidgetButton"> - <button type="button" class="oe_button"> + <button type="button" class="oe_button oe_form_button" + t-att-tabindex="widget.node.attrs.tabindex" + t-att-autofocus="widget.node.attrs.autofocus"> <img t-if="widget.node.attrs.icon" t-att-src="_s + '/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/> <span t-if="widget.string"><t t-esc="widget.string"/></span> </button> @@ -1248,17 +1242,57 @@ </t> </t> -<t t-name="SearchView"> - <form class="oe_forms"> - <t t-call="SearchView.render_lines"/> - <div class="oe_search-view-buttons"> - <button class="oe_button">Search</button> - <button class="oe_button" type="reset">Clear</button> - <select class="oe_search-view-filters-management"> - </select> - </div> - </form> +<t t-name="TranslateDialog"> + <table t-if="widget.view.translatable_fields" class="oe_form oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%"> + <tr> + <td class="oe_form_separator" width="1%" nowrap="nowrap"> + <div class="oe_horizontal_separator">Field</div> + </td> + <th t-foreach="widget.languages" align="left"> + <div class="oe_horizontal_separator"><t t-esc="name"/></div> + </th> + </tr> + <tr t-foreach="widget.view.translatable_fields" t-as="field" t-att-data-field="field.name"> + <td class="oe_form_group_cell" width="1%" nowrap="nowrap"> + <label class="oe_label"><t t-esc="field.node.attrs.string"/>:</label> + </td> + <td t-foreach="widget.languages" t-as="lg" class="oe_form_group_cell"> + <input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" data-value="" class="oe_trad_field" style="width: 100%"/> + <textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" data-value="" class="oe_trad_field" style="width: 100%"></textarea> + </td> + </tr> + </table> +</t> + +<t t-name="One2Many.viewmanager" t-extend="ViewManager"> + <t t-jquery="span.oe_view_title_text" t-operation="replace"/> + + <t t-jquery=".oe-view-manager-header"> + this.attr('t-if', 'views.length != 1'); + </t> +</t> +<t t-name="One2Many.formview" t-extend="FormView"> + <t t-jquery=".oe_form_buttons" t-operation="inner"> + <button type="button" class="oe_button oe_form_button_create">Add</button> + </t> +</t> +<t t-name="One2Many.listview" t-extend="ListView"> + <t t-jquery="thead.ui-widget-header > tr:first"> + this.removeAttr('t-if'); + </t> + + <t t-jquery="tfoot > tr:last-child" t-operation="replace"/> + + <t t-jquery="td.oe-actions"> + this.removeAttr('t-if'); + </t> + <t t-jquery="td.oe-actions" t-operation="prepend"> + <h3 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h3> + </t> </t> + +<div t-name="SearchView" class="oe_searchview"> +</div> <t t-name="SearchView.managed-filters"> <option class="oe-filters-title" value="">Filters</option> <optgroup label="-- Filters --"> @@ -1314,10 +1348,28 @@ <br t-if="attrs.string"/> <t t-esc="attrs.string"/> </button> -<span t-name="SearchView.filters" class="filter_label_group" - ><t t-foreach="filters" t-as="filter" - ><t t-raw="filter.render(defaults)"/></t -></span> +<ul t-name="SearchView.filters"> + <li t-foreach="widget.filters" t-as="filter"> + <t t-esc="filter.attrs.string or filter.attrs.name or 'Ω'"/> + </li> +</ul> +<t t-name="SearchView.filters.facet"> + <div class="category oe_filter_category"><t t-esc="facet.get('category')"/></div> + + <t t-set="val" t-value="facet.get('json')"/> + + <div t-if="!(val instanceof Array)" class="search_facet_input_container"> + <t t-esc="facet.get('value')"/> + </div> + <t t-if="val instanceof Array"> + <div class="search_facet_input_container" + t-foreach="facet.get('json')" t-as="filter"> + <t t-esc="filter.attrs.string || filter.attrs.name"/> + </div> + </t> + + <div class="search_facet_remove VS-icon VS-icon-cancel"/> +</t> <t t-name="SearchView.field"> <label t-att-class="'oe_label' + (attrs.help ? '_help' : '')" t-att-title="attrs.help" @@ -1375,8 +1427,7 @@ </div> </t> <t t-name="SearchView.util.expand"> - <div t-att-class="'searchview_group ' + (expand == '0' ? 'folded' : 'expanded')" - t-att-id="element_id"> + <div t-att-class="'searchview_group ' + (expand == '0' ? 'folded' : 'expanded')"> <a t-if="label" class="searchview_group_string" href="#"> <t t-esc="label"/> </a> @@ -1394,34 +1445,18 @@ </t> </t> </t> -<t t-name="SearchView.extended_search"> - <t t-call="SearchView.util.expand"> - <t t-set="expand" t-value="false"/> - <t t-set="label">Advanced Filters</t> - <t t-set="content"> - <div class="searchview_extended_groups_list"> - </div> - </t> - </t> -</t> -<t t-name="SearchView.extended_search.group"> - <div t-att-id="widget.element_id" class="searchview_extended_group"> - <select class="searchview_extended_group_choice"> - <option value="any">Any of the following conditions must match</option> - <option value="all">All the following conditions must match</option> - <option value="none">None of the following conditions must match</option> - </select> - <a class="searchview_extended_delete_group" - href="javascript:void(0)"><span></span></a> - <div class="searchview_extended_propositions_list"> - </div> - <a class="searchview_extended_add_proposition" href="javascript:void(0)"> - <span>Add condition</span></a> - <div class="oe_adv_filters_and"><span>and</span></div> - </div> -</t> +<div t-name="SearchView.advanced" class="oe_searchview_advanced"> + <h4>Advanced Search...</h4> + <form> + <ul> + + </ul> + <button class="oe_add_condition" type="button">Add a condition</button> + <button class="oe_apply" type="submit">Apply</button> + </form> +</div> <t t-name="SearchView.extended_search.proposition"> - <div t-att-id="widget.element_id"> + <li> <select class="searchview_extended_prop_field"> <t t-foreach="widget.attrs.fields" t-as="field"> <option t-if="typeof field.store === 'undefined' || field.store || field.fnct_search" @@ -1435,7 +1470,7 @@ <span class="searchview_extended_prop_value"/> <a class="searchview_extended_delete_prop" href="javascript:void(0)"><span> </span></a> - </div> + </li> </t> <t t-name="SearchView.extended_search.proposition.char"> <input t-att-id="widget.element_id" class="field_char"/> @@ -1459,20 +1494,20 @@ </select> </t> <t t-name="SelectCreatePopup"> - <div t-att-id="widget.element_id"> + <div> <table style="width:100%"> <tr style="width:100%"> <td style="width:100%"> - <div t-att-id="widget.element_id + '_search'" style="width:100%"></div> + <div class="oe-select-create-popup-view-search" style="width:100%"></div> </td> </tr> <tr style="width:100%"> <td style="width:100%"> - <div t-att-id="widget.element_id + '_view_list'" style="width:100%"></div> + <div class="oe-select-create-popup-view-list" style="width:100%"></div> </td> </tr> </table> - <div t-att-id="widget.element_id + '_view_form'" style="width:100%"></div> + <div class="oe-select-create-popup-view-form" style="width:100%"></div> </div> </t> <t t-name="SelectCreatePopup.search.buttons"> @@ -1490,8 +1525,8 @@ <button type="button" class="oe_button oe_selectcreatepopup-form-close">Cancel</button> </t> <t t-name="FormOpenPopup"> - <div t-att-id="widget.element_id"> - <div t-att-id="widget.element_id + '_view_form'" style="width:100%"></div> + <div> + <div class="oe-form-open-popup-form-view" style="width:100%"></div> </div> </t> <t t-name="FormOpenPopup.form.buttons"> @@ -1507,21 +1542,7 @@ <td t-if="edited and !options.deletable" class="oe-listview-padding"/> </t> </t> -<t t-name="ListView.row.frame"> - <t t-foreach="widget.table" t-as="row"> - <t t-foreach="row" t-as="td"> - <td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined" - t-att-valign="td.table ? 'top' : undefined" - t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class} oe-field-cell" - > - <t t-raw="td.render()"/> - </td> - </t> - </t> - <td> - <button class="oe-edit-row-save" type="button"> </button> - </td> -</t> + <t t-name="view_editor"> <table class="oe_view_editor"> <t t-call="view_editor.row"/> @@ -1585,6 +1606,7 @@ <t t-name="vieweditor_boolean"> <input type="checkbox" t-att-id="widget.name"/> </t> + <t t-name="ExportView"> <a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a> </t> @@ -1636,7 +1658,6 @@ </td> </tr> </table> - <table t-name="ExportTreeView-Secondary" id="field-tree-structure" class="oe_export_fields_selector_export" cellspacing="0" cellpadding="0"> @@ -1670,7 +1691,6 @@ </table> </td> </tr> - <t t-name="ExportNewList"> <tr> <th><label>Save as:</label></th> @@ -1678,7 +1698,6 @@ <td><button class="oe_button oe_export_button_export" id="add_export_list">Ok</button></td> </tr> </t> - <t t-name="Exists.ExportList"> <label for="saved_export_list">Saved exports:</label> @@ -1690,30 +1709,6 @@ </select> <button class="oe_button oe_export_button_export" id="delete_export_list" type="button">Delete</button> </t> -<t t-name="Change_Pwd"> - <form name="change_password_form" class="oe_forms" method="POST"> - <table align="center"> - <tr> - <td><label for="old_pwd">Old Password:</label></td> - <td><input type="password" name="old_pwd" - minlength="1" autofocus="autofocus"/></td> - </tr> - <tr> - <td><label for="new_password">New Password:</label></td> - <td><input type="password" name="new_password" - minlength="1" autofocus="autofocus"/></td> - </tr> - <tr> - <td><label for="confirm_pwd">Confirm Password:</label></td> - <td><input type="password" name="confirm_pwd" - minlength="1"/></td> - </tr> - <tr> - <td colspan="2" align="right"><button class="oe_button">Change Password</button></td> - </tr> - </table> - </form> -</t> <t t-name="ImportView"> <a id="importview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Import</a> @@ -1790,34 +1785,4 @@ </t> </t> -<t t-name="About-Page"> - <div> - <a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developper mode</a> - <h1 style="margin:0;">OpenERP</h1> - <h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3> - <p> - Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br /> - OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>. - </p> - <p> - Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a> - </p> - <p> - For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a> - </p> - </div> -</t> -<t t-name="FieldStatus.content"> - <ul class="oe-arrow-list"> - <t t-set="size" t-value="widget.to_show.length"/> - <t t-foreach="_.range(size)" t-as="i"> - <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe-arrow-list-selected' : ''"> - <span class="oe-arrow-list-before" t-if="i > 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i < size - 1"></span> - </li> - </t> - </ul> -</t> -<t t-name="EmptyComponent"> - <div></div> -</t> </templates> diff --git a/addons/web/static/test/class.js b/addons/web/static/test/class.js index ed977635a5f..faad4421e31 100644 --- a/addons/web/static/test/class.js +++ b/addons/web/static/test/class.js @@ -2,8 +2,8 @@ $(document).ready(function () { var openerp; module('web-class', { setup: function () { - openerp = window.openerp.init(); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); } }); test('Basic class creation', function () { diff --git a/addons/web/static/test/evals.js b/addons/web/static/test/evals.js new file mode 100644 index 00000000000..290a009f46d --- /dev/null +++ b/addons/web/static/test/evals.js @@ -0,0 +1,136 @@ +$(document).ready(function () { + var openerp; + + module("eval.contexts", { + setup: function () { + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); + } + }); + test('context_sequences', function () { + // Context n should have base evaluation context + all of contexts + // 0..n-1 in its own evaluation context + var active_id = 4; + var result = openerp.connection.test_eval_contexts([ + { + "__contexts": [ + { + "department_id": false, + "lang": "en_US", + "project_id": false, + "section_id": false, + "tz": false, + "uid": 1 + }, + { "search_default_create_uid": 1 }, + {} + ], + "__eval_context": null, + "__ref": "compound_context" + }, + { + "active_id": active_id, + "active_ids": [ active_id ], + "active_model": "purchase.requisition" + }, + { + "__debug": "{'record_id' : active_id}", + "__id": "63e8e9bff8a6", + "__ref": "context" + } + ]); + + deepEqual(result, { + department_id: false, + lang: 'en_US', + project_id: false, + section_id: false, + tz: false, + uid: 1, + search_default_create_uid: 1, + active_id: active_id, + active_ids: [active_id], + active_model: 'purchase.requisition', + record_id: active_id + }); + }); + test('non-literal_eval_contexts', function () { + var result = openerp.connection.test_eval_contexts([{ + "__ref": "compound_context", + "__contexts": [ + {"__ref": "context", "__debug": "{'type':parent.type}", + "__id": "462b9dbed42f"} + ], + "__eval_context": { + "__ref": "compound_context", + "__contexts": [{ + "__ref": "compound_context", + "__contexts": [ + {"__ref": "context", "__debug": "{'type': type}", + "__id": "16a04ed5a194"} + ], + "__eval_context": { + "__ref": "compound_context", + "__contexts": [ + {"lang": "en_US", "tz": false, "uid": 1, + "journal_type": "sale", "section_id": false, + "default_type": "out_invoice", + "type": "out_invoice", "department_id": false}, + {"id": false, "journal_id": 10, + "number": false, "type": "out_invoice", + "currency_id": 1, "partner_id": 4, + "fiscal_position": false, + "date_invoice": false, "period_id": false, + "payment_term": false, "reference_type": "none", + "reference": false, "account_id": 440, + "name": false, "invoice_line": [], + "tax_line": [], "amount_untaxed": 0, + "amount_tax": 0, "reconciled": false, + "amount_total": 0, "state": "draft", + "residual": 0, "company_id": 1, + "date_due": false, "user_id": 1, + "partner_bank_id": false, "origin": false, + "move_id": false, "comment": false, + "payment_ids": [[6, false, []]], + "active_id": false, "active_ids": [], + "active_model": "account.invoice", + "parent": {}} + ], "__eval_context": null} + }, { + "id": false, + "product_id": 4, + "name": "[PC1] Basic PC", + "quantity": 1, + "uos_id": 1, + "price_unit": 100, + "account_id": 853, + "discount": 0, + "account_analytic_id": false, + "company_id": false, + "note": false, + "invoice_line_tax_id": [[6, false, [1]]], + "active_id": false, + "active_ids": [], + "active_model": "account.invoice.line", + "parent": { + "id": false, "journal_id": 10, "number": false, + "type": "out_invoice", "currency_id": 1, + "partner_id": 4, "fiscal_position": false, + "date_invoice": false, "period_id": false, + "payment_term": false, "reference_type": "none", + "reference": false, "account_id": 440, "name": false, + "tax_line": [], "amount_untaxed": 0, "amount_tax": 0, + "reconciled": false, "amount_total": 0, + "state": "draft", "residual": 0, "company_id": 1, + "date_due": false, "user_id": 1, + "partner_bank_id": false, "origin": false, + "move_id": false, "comment": false, + "payment_ids": [[6, false, []]]} + }], + "__eval_context": null + } + }]); + deepEqual(result, {type: 'out_invoice'}); + }); +}); diff --git a/addons/web/static/test/form.js b/addons/web/static/test/form.js index 8317c7bfcd2..aace926a96a 100644 --- a/addons/web/static/test/form.js +++ b/addons/web/static/test/form.js @@ -2,8 +2,9 @@ $(document).ready(function () { var openerp; module("form.widget", { setup: function () { - openerp = window.openerp.init(true); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); window.openerp.web.chrome(openerp); // views loader stuff window.openerp.web.data(openerp); diff --git a/addons/web/static/test/formats.js b/addons/web/static/test/formats.js index c0d6798628d..4fe07ff0a9c 100644 --- a/addons/web/static/test/formats.js +++ b/addons/web/static/test/formats.js @@ -3,8 +3,9 @@ $(document).ready(function () { module('server-formats', { setup: function () { - openerp = window.openerp.init(); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); window.openerp.web.dates(openerp); } }); @@ -40,8 +41,9 @@ $(document).ready(function () { module('web-formats', { setup: function () { - openerp = window.openerp.init(); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); window.openerp.web.dates(openerp); window.openerp.web.formats(openerp); } @@ -206,8 +208,9 @@ $(document).ready(function () { }); module('custom-date-formats', { setup: function () { - openerp = window.openerp.init(); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); window.openerp.web.dates(openerp); window.openerp.web.formats(openerp); } diff --git a/addons/web/static/test/fulltest.html b/addons/web/static/test/fulltest.html index f27adfa7df9..a031325966a 100644 --- a/addons/web/static/test/fulltest.html +++ b/addons/web/static/test/fulltest.html @@ -24,10 +24,11 @@ <script src="/web/static/lib/qweb/qweb2.js"></script> - <script src="/web/static/lib/py.parse/lib/py.js"></script> + <script src="/web/static/lib/py.js/lib/py.js"></script> <script src="/web/static/src/js/boot.js"></script> - <script src="/web/static/src/js/core.js"></script> + <script src="/web/static/src/js/corelib.js"></script> + <script src="/web/static/src/js/coresetup.js"></script> <script src="/web/static/src/js/dates.js"></script> <script src="/web/static/src/js/formats.js"></script> <script src="/web/static/src/js/chrome.js"></script> diff --git a/addons/web/static/test/list-utils.js b/addons/web/static/test/list-utils.js index 8cc34454cd5..7890e71c6bc 100644 --- a/addons/web/static/test/list-utils.js +++ b/addons/web/static/test/list-utils.js @@ -10,7 +10,13 @@ $(document).ready(function () { }; module('list-events', { setup: function () { - openerp = window.openerp.init(); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); + window.openerp.web.chrome(openerp); + // views loader stuff + window.openerp.web.data(openerp); + window.openerp.web.views(openerp); window.openerp.web.list(openerp); } }); @@ -90,7 +96,13 @@ $(document).ready(function () { module('list-records', { setup: function () { - openerp = window.openerp.init(); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); + window.openerp.web.chrome(openerp); + // views loader stuff + window.openerp.web.data(openerp); + window.openerp.web.views(openerp); window.openerp.web.list(openerp); } }); @@ -123,7 +135,13 @@ $(document).ready(function () { module('list-collections-degenerate', { setup: function () { - openerp = window.openerp.init(); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); + window.openerp.web.chrome(openerp); + // views loader stuff + window.openerp.web.data(openerp); + window.openerp.web.views(openerp); window.openerp.web.list(openerp); } }); @@ -245,7 +263,13 @@ $(document).ready(function () { module('list-hofs', { setup: function () { - openerp = window.openerp.init(); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); + window.openerp.web.chrome(openerp); + // views loader stuff + window.openerp.web.data(openerp); + window.openerp.web.views(openerp); window.openerp.web.list(openerp); } }); diff --git a/addons/web/static/test/onchange.js b/addons/web/static/test/onchange.js deleted file mode 100644 index 8074c755bd3..00000000000 --- a/addons/web/static/test/onchange.js +++ /dev/null @@ -1,74 +0,0 @@ -$(document).ready(function () { - var openerp, - make_form = function (default_values) { - var fields = {}; - _(default_values).each(function (value, name) { - fields[name] = value instanceof Function ? value : { - get_on_change_value: function () { return value; } - }; - }); - return _.extend(new openerp.web.FormView(null, {}), - {fields: fields}); - }; - module("form.onchange", { - setup: function () { - openerp = window.openerp.init(true); - window.openerp.web.core(openerp); - window.openerp.web.chrome(openerp); - // views loader stuff - window.openerp.web.data(openerp); - window.openerp.web.views(openerp); - window.openerp.web.list(openerp); - window.openerp.web.form(openerp); - } - }); - test('Parse args-less onchange', function () { - var f = new openerp.web.FormView(null, {}); - var result = f.parse_on_change('on_change_foo()', {}); - equal(result.method, 'on_change_foo'); - deepEqual(result.args, []); - }); - test('Parse 1-arg onchange', function () { - var f = make_form({foo: 3}); - var result = f.parse_on_change('on_change_foo(foo)', {}); - equal(result.method, 'on_change_foo'); - deepEqual(result.args, [3]); - }); - test('Parse 2-args onchange', function () { - var f = make_form({foo: 3, bar: 'qux'}); - var result = f.parse_on_change('on_change_foo(bar, foo)', {}); - equal(result.method, 'on_change_foo'); - deepEqual(result.args, ['qux', 3]); - }); - test('Literal null', function () { - var f = make_form(); - var result = f.parse_on_change('on_null(None)', {}); - deepEqual(result.args, [null]); - }); - test('Literal true', function () { - var f = make_form(); - var result = f.parse_on_change('on_null(True)', {}); - deepEqual(result.args, [true]); - }); - test('Literal false', function () { - var f = make_form(); - var result = f.parse_on_change('on_null(False)', {}); - deepEqual(result.args, [false]); - }); - test('Literal string', function () { - var f = make_form(); - var result = f.parse_on_change('on_str("foo")', {}); - deepEqual(result.args, ['foo']); - var result2 = f.parse_on_change("on_str('foo')", {}); - deepEqual(result2.args, ['foo']); - }); - test('Literal number', function () { - var f = make_form(); - var result = f.parse_on_change('on_str(42)', {}); - deepEqual(result.args, [42]); - var result2 = f.parse_on_change("on_str(-25)", {}); - deepEqual(result2.args, [-25]); - var result3 = f.parse_on_change("on_str(25.02)", {}); - deepEqual(result3.args, [25.02]); - }); -}); diff --git a/addons/web/static/test/registry.js b/addons/web/static/test/registry.js index d0a1d10385e..da0cb952783 100644 --- a/addons/web/static/test/registry.js +++ b/addons/web/static/test/registry.js @@ -2,8 +2,8 @@ $(document).ready(function () { var openerp; module('Registry', { setup: function () { - openerp = window.openerp.init(true); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); openerp.web.Foo = {}; openerp.web.Bar = {}; openerp.web.Foo2 = {}; diff --git a/addons/web/static/test/rpc.js b/addons/web/static/test/rpc.js index 7dbd3239abd..8a49a48685f 100644 --- a/addons/web/static/test/rpc.js +++ b/addons/web/static/test/rpc.js @@ -3,8 +3,9 @@ $(document).ready(function () { module('Misordered resolution management', { setup: function () { - openerp = window.openerp.init(); - window.openerp.web.core(openerp); + openerp = window.openerp.init([]); + window.openerp.web.corelib(openerp); + window.openerp.web.coresetup(openerp); window.openerp.web.data(openerp); } }); diff --git a/addons/web/static/test/test.html b/addons/web/static/test/test.html index 004ba0479e1..84dd3ee7f27 100644 --- a/addons/web/static/test/test.html +++ b/addons/web/static/test/test.html @@ -24,10 +24,11 @@ <script src="/web/static/lib/qweb/qweb2.js"></script> - <script src="/web/static/lib/py.parse/lib/py.js"></script> + <script src="/web/static/lib/py.js/lib/py.js"></script> <script src="/web/static/src/js/boot.js"></script> - <script src="/web/static/src/js/core.js"></script> + <script src="/web/static/src/js/corelib.js"></script> + <script src="/web/static/src/js/coresetup.js"></script> <script src="/web/static/src/js/dates.js"></script> <script src="/web/static/src/js/formats.js"></script> <script src="/web/static/src/js/chrome.js"></script> @@ -50,6 +51,6 @@ <script type="text/javascript" src="/web/static/test/form.js"></script> <script type="text/javascript" src="/web/static/test/list-utils.js"></script> <script type="text/javascript" src="/web/static/test/formats.js"></script> - <script type="text/javascript" src="/web/static/test/onchange.js"></script> <script type="text/javascript" src="/web/static/test/rpc.js"></script> + <script type="text/javascript" src="/web/static/test/evals.js"></script> </html> diff --git a/addons/web_calendar/i18n/bs.po b/addons/web_calendar/i18n/bs.po new file mode 100644 index 00000000000..c6b1d5e007e --- /dev/null +++ b/addons/web_calendar/i18n/bs.po @@ -0,0 +1,41 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-15 00:09+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-16 04:50+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:11 +msgid "Calendar" +msgstr "Kalendar" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:466 +#: addons/web_calendar/static/src/js/calendar.js:467 +msgid "Responsible" +msgstr "Odgovoran" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:504 +#: addons/web_calendar/static/src/js/calendar.js:505 +msgid "Navigator" +msgstr "Navigator" + +#. openerp-web +#: addons/web_calendar/static/src/xml/web_calendar.xml:5 +#: addons/web_calendar/static/src/xml/web_calendar.xml:6 +msgid " " +msgstr " " diff --git a/addons/web_calendar/i18n/en_AU.po b/addons/web_calendar/i18n/en_AU.po new file mode 100644 index 00000000000..fbddd79ab58 --- /dev/null +++ b/addons/web_calendar/i18n/en_AU.po @@ -0,0 +1,41 @@ +# English (Australia) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-09 03:26+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: English (Australia) <en_AU@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-04-10 05:02+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:11 +msgid "Calendar" +msgstr "Calendar" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:466 +#: addons/web_calendar/static/src/js/calendar.js:467 +msgid "Responsible" +msgstr "Responsible" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:504 +#: addons/web_calendar/static/src/js/calendar.js:505 +msgid "Navigator" +msgstr "Navigator" + +#. openerp-web +#: addons/web_calendar/static/src/xml/web_calendar.xml:5 +#: addons/web_calendar/static/src/xml/web_calendar.xml:6 +msgid " " +msgstr " " diff --git a/addons/web_calendar/i18n/es_CL.po b/addons/web_calendar/i18n/es_CL.po new file mode 100644 index 00000000000..5106845e96e --- /dev/null +++ b/addons/web_calendar/i18n/es_CL.po @@ -0,0 +1,41 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-14 15:13+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:11 +msgid "Calendar" +msgstr "Calendario" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:466 +#: addons/web_calendar/static/src/js/calendar.js:467 +msgid "Responsible" +msgstr "Responsable" + +#. openerp-web +#: addons/web_calendar/static/src/js/calendar.js:504 +#: addons/web_calendar/static/src/js/calendar.js:505 +msgid "Navigator" +msgstr "Navegador" + +#. openerp-web +#: addons/web_calendar/static/src/xml/web_calendar.xml:5 +#: addons/web_calendar/static/src/xml/web_calendar.xml:6 +msgid " " +msgstr " " diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index eee6650d459..8bd83c0bb30 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -2,12 +2,12 @@ * OpenERP web_calendar *---------------------------------------------------------*/ -openerp.web_calendar = function(openerp) { -var _t = openerp.web._t, - _lt = openerp.web._lt; -var QWeb = openerp.web.qweb; -openerp.web.views.add('calendar', 'openerp.web_calendar.CalendarView'); -openerp.web_calendar.CalendarView = openerp.web.View.extend({ +openerp.web_calendar = function(instance) { +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; +instance.web.views.add('calendar', 'instance.web_calendar.CalendarView'); +instance.web_calendar.CalendarView = instance.web.View.extend({ display_name: _lt('Calendar'), // Dhtmlx scheduler ? init: function(parent, dataset, view_id, options) { @@ -18,10 +18,11 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ this.model = dataset.model; this.fields_view = {}; this.view_id = view_id; + this.view_type = 'calendar'; this.has_been_loaded = $.Deferred(); this.creating_event_id = null; this.dataset_events = []; - this.form_dialog = new openerp.web_calendar.CalendarFormDialog(this, { + this.form_dialog = new instance.web_calendar.CalendarFormDialog(this, { destroy_on_close: false, width: '80%', min_width: 850 @@ -40,7 +41,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ }, start: function() { this._super(); - return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': true}, this.on_loaded); + return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}, this.on_loaded); }, destroy: function() { scheduler.clearAll(); @@ -99,21 +100,14 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ } this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view})); - if (this.options.sidebar && this.options.sidebar_id) { - this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); - this.sidebar.start(); - this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this); - this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this); - this.sidebar.add_toolbar(this.fields_view.toolbar); - this.set_common_sidebar_sections(this.sidebar); - this.sidebar.do_unfold(); - this.sidebar.do_fold.add_last(this.refresh_scheduler); - this.sidebar.do_unfold.add_last(this.refresh_scheduler); - this.sidebar.do_toggle.add_last(this.refresh_scheduler); + this.init_scheduler(); + + if (! this.sidebar && this.options.$sidebar) { + this.sidebar = new instance.web_calendar.Sidebar(this); + this.has_been_loaded.pipe(this.sidebar.appendTo(this.options.$sidebar)); } - this.init_scheduler(); - this.has_been_loaded.resolve(); + return this.has_been_loaded.resolve(); }, init_scheduler: function() { var self = this; @@ -147,17 +141,6 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ scheduler.attachEvent('onViewChange', this.on_view_changed); this.refresh_scheduler(); - - if (this.options.sidebar) { - this.mini_calendar = scheduler.renderCalendar({ - container: this.sidebar.navigator.element_id, - navigation: true, - date: scheduler._date, - handler: function(date, calendar) { - scheduler.setCurrentView(date, 'day'); - } - }); - } }, on_view_changed: function(mode, date) { this.$element.removeClass('oe_cal_day oe_cal_week oe_cal_month').addClass('oe_cal_' + mode); @@ -175,8 +158,8 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ scheduler.setCurrentView(scheduler._date); }, refresh_minical: function() { - if (this.options.sidebar) { - scheduler.updateCalendar(this.mini_calendar); + if (this.sidebar) { + scheduler.updateCalendar(this.sidebar.mini_calendar); } }, reload_event: function(id) { @@ -224,23 +207,23 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ } if (this.fields[this.date_start]['type'] == 'date') { - evt[this.date_start] = openerp.web.auto_str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss'); + evt[this.date_start] = instance.web.auto_str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss'); } if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') { - evt[this.date_stop] = openerp.web.auto_str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss'); + evt[this.date_stop] = instance.web.auto_str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss'); } res_events.push(this.convert_event(evt)); } scheduler.parse(res_events, 'json'); this.refresh_scheduler(); this.refresh_minical(); - if (!no_filter_reload && this.options.sidebar) { - this.sidebar.responsible.on_events_loaded(sidebar_items); + if (!no_filter_reload && this.sidebar) { + this.sidebar.filter.on_events_loaded(sidebar_items); } }, convert_event: function(evt) { - var date_start = openerp.web.str_to_datetime(evt[this.date_start]), - date_stop = this.date_stop ? openerp.web.str_to_datetime(evt[this.date_stop]) : null, + var date_start = instance.web.str_to_datetime(evt[this.date_start]), + date_stop = this.date_stop ? instance.web.str_to_datetime(evt[this.date_stop]) : null, date_delay = evt[this.date_delay] || 1.0, res_text = '', res_description = []; @@ -308,7 +291,6 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ var ffield = form.fields[field_name]; ffield.reset(); $.when(ffield.set_value(data[field_name])).then(function() { - ffield.validate(); ffield.dirty = true; form.do_onchange(ffield); }); @@ -344,7 +326,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ var index = this.dataset.get_id_index(event_id); if (index !== null) { this.dataset.index = index; - this.do_switch_view('page'); + this.do_switch_view('form'); } else if (scheduler.getState().mode === 'month') { var event_obj = scheduler.getEvent(event_id); if (event_obj._length === 1) { @@ -371,9 +353,9 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ var data = { name: event_obj.text }; - data[this.date_start] = openerp.web.datetime_to_str(event_obj.start_date); + data[this.date_start] = instance.web.datetime_to_str(event_obj.start_date); if (this.date_stop) { - data[this.date_stop] = openerp.web.datetime_to_str(event_obj.end_date); + data[this.date_stop] = instance.web.datetime_to_str(event_obj.end_date); } if (this.date_delay) { var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000); @@ -400,7 +382,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ }); }, get_range_domain: function() { - var format = openerp.web.date_to_str, + var format = instance.web.date_to_str, domain = this.last_search[0].slice(0); domain.unshift([this.date_start, '>=', format(this.range_start.clone().addDays(-6))]); domain.unshift([this.date_start, '<=', format(this.range_stop.clone().addDays(6))]); @@ -410,25 +392,16 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({ var self = this; $.when(this.has_been_loaded).then(function() { self.$element.show(); - if (self.sidebar) { - self.sidebar.$element.show(); - } self.do_push_state({}); }); }, - do_hide: function () { - this._super(); - if (this.sidebar) { - this.sidebar.$element.hide(); - } - }, get_selected_ids: function() { // no way to select a record anyway return []; } }); -openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({ +instance.web_calendar.CalendarFormDialog = instance.web.Dialog.extend({ init: function(view, options, view_id, dataset) { this._super(view, options); this.dataset = dataset; @@ -438,8 +411,7 @@ openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({ start: function() { var self = this; this._super(); - this.form = new openerp.web.FormView(this, this.dataset, this.view_id, { - sidebar: false, + this.form = new instance.web.FormView(this, this.dataset, this.view_id, { pager: false }); this.form.appendTo(this.$element); @@ -466,19 +438,31 @@ openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({ } }); -openerp.web_calendar.SidebarResponsible = openerp.web.OldWidget.extend({ - // TODO: fme: in trunk, rename this class to SidebarFilter +instance.web_calendar.Sidebar = instance.web.Widget.extend({ + template: 'CalendarView.sidebar', + start: function() { + this._super(); + this.mini_calendar = scheduler.renderCalendar({ + container: this.$element.find('.oe_calendar_mini')[0], + navigation: true, + date: scheduler._date, + handler: function(date, calendar) { + scheduler.setCurrentView(date, 'day'); + } + }); + this.filter = new instance.web_calendar.SidebarFilter(this, this.getParent()); + this.filter.appendTo(this.$element.find('.oe_calendar_filter')); + } +}); +instance.web_calendar.SidebarFilter = instance.web.Widget.extend({ init: function(parent, view) { - var $section = parent.add_section(view.color_string, 'responsible'); - this.$div = $('<div></div>'); - $section.append(this.$div); - this._super(parent, $section.attr('id')); + this._super(parent); this.view = view; this.$element.delegate('input:checkbox', 'change', this.on_filter_click); }, on_events_loaded: function(filters) { var selected_filters = this.view.selected_filters.slice(0); - this.$div.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters })); + this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters })); this.$element.find('div.oe_calendar_responsible input').each(function() { if (_.indexOf(selected_filters, $(this).val()) > -1) { $(this).click(); @@ -505,15 +489,6 @@ openerp.web_calendar.SidebarResponsible = openerp.web.OldWidget.extend({ } }); -openerp.web_calendar.SidebarNavigator = openerp.web.OldWidget.extend({ - init: function(parent, view) { - var $section = parent.add_section(_t('Navigator'), 'navigator'); - this._super(parent, $section.attr('id')); - this.view = view; - }, - on_events_loaded: function(events) { - } -}); }; // DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner')) diff --git a/addons/web_calendar/static/src/xml/web_calendar.xml b/addons/web_calendar/static/src/xml/web_calendar.xml index 53bb7109786..422c37dcf0a 100644 --- a/addons/web_calendar/static/src/xml/web_calendar.xml +++ b/addons/web_calendar/static/src/xml/web_calendar.xml @@ -1,5 +1,6 @@ <template> - <t t-name="CalendarView"> +<t t-name="CalendarView"> + <div class="oe_calendar"> <div id="openerp_scheduler" class="dhx_cal_container" style="height: 600px;"> <div class="dhx_cal_navline"> <div class="dhx_cal_prev_button">&nbsp;</div> @@ -15,6 +16,15 @@ <div class="dhx_cal_data"> </div> </div> + </div> +</t> +<t t-name="CalendarView.sidebar"> + <div class="oe_calendar_sidebar" style="width: 500px"> + <div class="oe_calendar_mini oe_left" style="width: 240px"/> + <div class="oe_calendar_filter oe_right" style="width: 240px"> + <h3><t t-esc="widget.getParent().color_string"/></h3> + </div> + </div> </t> <t t-name="CalendarView.sidebar.responsible"> <div t-foreach="filters" class="oe_calendar_responsible" t-attf-style="background: #{filters_value.color}"> diff --git a/addons/web_dashboard/i18n/bs.po b/addons/web_dashboard/i18n/bs.po new file mode 100644 index 00000000000..c78733fbe8a --- /dev/null +++ b/addons/web_dashboard/i18n/bs.po @@ -0,0 +1,111 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-15 00:11+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-16 04:50+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:63 +msgid "Edit Layout" +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:109 +msgid "Are you sure you want to remove this item ?" +msgstr "Jeste li sigurni da želite ukloniti predmet?" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:316 +msgid "Uncategorized" +msgstr "Nekategorisano" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:324 +#, python-format +msgid "Execute task \"%s\"" +msgstr "Izvrši zadatak \"%s\"" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:325 +msgid "Mark this task as done" +msgstr "Označi zadatak kao izvršen" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4 +msgid "Reset Layout.." +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6 +msgid "Reset" +msgstr "Resetuj" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8 +msgid "Change Layout.." +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10 +msgid "Change Layout" +msgstr "Izmijeni izgled" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27 +msgid " " +msgstr " " + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28 +msgid "Create" +msgstr "Napravi" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39 +msgid "Choose dashboard layout" +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62 +msgid "progress:" +msgstr "Napredak:" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67 +msgid "" +"Click on the functionalites listed below to launch them and configure your " +"system" +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110 +msgid "Welcome to OpenERP" +msgstr "Dobro došli na OpenERP" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118 +msgid "Remember to bookmark" +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119 +msgid "This url" +msgstr "Ovaj url" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121 +msgid "Your login:" +msgstr "Vaš login:" diff --git a/addons/web_dashboard/i18n/es_CL.po b/addons/web_dashboard/i18n/es_CL.po new file mode 100644 index 00000000000..33714bf8473 --- /dev/null +++ b/addons/web_dashboard/i18n/es_CL.po @@ -0,0 +1,113 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-14 15:21+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:63 +msgid "Edit Layout" +msgstr "Modificar diseño" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:109 +msgid "Are you sure you want to remove this item ?" +msgstr "¿Esta seguro que quiere eliminar este item?" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:316 +msgid "Uncategorized" +msgstr "Sin categoría" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:324 +#, python-format +msgid "Execute task \"%s\"" +msgstr "Ejecutar tarea \"%s\"" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:325 +msgid "Mark this task as done" +msgstr "Marcar esta tarea como terminada" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4 +msgid "Reset Layout.." +msgstr "Reiniciar disposición" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6 +msgid "Reset" +msgstr "Reiniciar" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8 +msgid "Change Layout.." +msgstr "Cambiar Disposición.." + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10 +msgid "Change Layout" +msgstr "Cambiar disposición" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27 +msgid " " +msgstr " " + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28 +msgid "Create" +msgstr "Crear" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39 +msgid "Choose dashboard layout" +msgstr "Elegir disposición del tablero" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62 +msgid "progress:" +msgstr "progreso:" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67 +msgid "" +"Click on the functionalites listed below to launch them and configure your " +"system" +msgstr "" +"Haga click en las funcionalidades listadas debajo para lanzarlas y " +"configurar su sistema" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110 +msgid "Welcome to OpenERP" +msgstr "Bienvenido a OpenERP" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118 +msgid "Remember to bookmark" +msgstr "Recordar en marcadores" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119 +msgid "This url" +msgstr "Esta url" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121 +msgid "Your login:" +msgstr "Su inicio de sesión:" diff --git a/addons/web_dashboard/i18n/sv.po b/addons/web_dashboard/i18n/sv.po new file mode 100644 index 00000000000..dd70f52088c --- /dev/null +++ b/addons/web_dashboard/i18n/sv.po @@ -0,0 +1,112 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-17 09:21+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:63 +msgid "Edit Layout" +msgstr "Ändra layout" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:109 +msgid "Are you sure you want to remove this item ?" +msgstr "Är du säker på att du vill radera?" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:316 +msgid "Uncategorized" +msgstr "Okategoriserad" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:324 +#, python-format +msgid "Execute task \"%s\"" +msgstr "Utför åtgärd \"%s\"" + +#. openerp-web +#: addons/web_dashboard/static/src/js/dashboard.js:325 +msgid "Mark this task as done" +msgstr "Markera denna uppgift som färdig" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4 +msgid "Reset Layout.." +msgstr "Återställ layouten" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6 +msgid "Reset" +msgstr "Nollställ" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8 +msgid "Change Layout.." +msgstr "Ändra layout..." + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10 +msgid "Change Layout" +msgstr "Ändra layout" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27 +msgid " " +msgstr "" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28 +msgid "Create" +msgstr "Skapa" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39 +msgid "Choose dashboard layout" +msgstr "Välj infopanellayout" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:62 +msgid "progress:" +msgstr "framsteg:" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:67 +msgid "" +"Click on the functionalites listed below to launch them and configure your " +"system" +msgstr "" +"Välj bland funktionerna nedan för att starta och konfigurera ditt system" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:110 +msgid "Welcome to OpenERP" +msgstr "Välkommen till OpenERP" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:118 +msgid "Remember to bookmark" +msgstr "Kom ihåg att spara ett bokmärke till" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:119 +msgid "This url" +msgstr "denna url" + +#. openerp-web +#: addons/web_dashboard/static/src/xml/web_dashboard.xml:121 +msgid "Your login:" +msgstr "Din inloggning:" diff --git a/addons/web_dashboard/static/src/js/dashboard.js b/addons/web_dashboard/static/src/js/dashboard.js index bae74276a24..f0c04d6d270 100644 --- a/addons/web_dashboard/static/src/js/dashboard.js +++ b/addons/web_dashboard/static/src/js/dashboard.js @@ -1,13 +1,13 @@ -openerp.web_dashboard = function(openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t; +openerp.web_dashboard = function(instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; -if (!openerp.web_dashboard) { +if (!instance.web_dashboard) { /** @namespace */ - openerp.web_dashboard = {}; + instance.web_dashboard = {}; } -openerp.web.form.DashBoard = openerp.web.form.Widget.extend({ +instance.web.form.DashBoard = instance.web.form.Widget.extend({ init: function(view, node) { this._super(view, node); this.form_template = 'DashBoard'; @@ -56,7 +56,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({ var qdict = { current_layout : this.$element.find('.oe-dashboard').attr('data-layout') }; - var $dialog = openerp.web.dialog($('<div>'), { + var $dialog = instance.web.dialog($('<div>'), { modal: true, title: _t("Edit Layout"), width: 'auto', @@ -188,7 +188,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({ selectable: false } }; - var am = new openerp.web.ActionManager(this), + var am = new instance.web.ActionManager(this), // FIXME: ideally the dashboard view shall be refactored like kanban. $action = $('#' + this.view.element_id + '_action_' + index); $action.parent().data('action_attrs', action_attrs); @@ -249,7 +249,7 @@ openerp.web.form.DashBoard = openerp.web.form.Widget.extend({ action_manager.do_action(view_manager.action); } }); -openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({ +instance.web.form.DashBoardLegacy = instance.web.form.DashBoard.extend({ renderElement: function() { if (this.node.tag == 'hpaned') { this.node.attrs.style = '2-1'; @@ -273,28 +273,27 @@ openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({ } }); -openerp.web.form.widgets.add('hpaned', 'openerp.web.form.DashBoardLegacy'); -openerp.web.form.widgets.add('vpaned', 'openerp.web.form.DashBoardLegacy'); -openerp.web.form.widgets.add('board', 'openerp.web.form.DashBoard'); +instance.web.form.tags.add('hpaned', 'instance.web.form.DashBoardLegacy'); +instance.web.form.tags.add('vpaned', 'instance.web.form.DashBoardLegacy'); +instance.web.form.tags.add('board', 'instance.web.form.DashBoard'); /* * ConfigOverview * This client action designed to be used as a dashboard widget display * ir.actions.todo in a fancy way */ -openerp.web.client_actions.add( 'board.config.overview', 'openerp.web_dashboard.ConfigOverview'); -openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({ +instance.web.client_actions.add( 'board.config.overview', 'instance.web_dashboard.ConfigOverview'); +instance.web_dashboard.ConfigOverview = instance.web.View.extend({ template: 'ConfigOverview', init: function (parent) { this._super(parent); - this.user = _.extend(new openerp.web.DataSet(this, 'res.users'), { + this.user = _.extend(new instance.web.DataSet(this, 'res.users'), { index: 0, ids: [this.session.uid] }); - this.dataset = new openerp.web.DataSetSearch(this, 'ir.actions.todo'); + this.dataset = new instance.web.DataSetSearch(this, 'ir.actions.todo'); }, start: function () { - this._super(); var self = this; return this.user.read_index(['groups_id']).pipe(function(record) { var todos_filter = [ @@ -364,15 +363,15 @@ openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({ * This client action designed to be used as a dashboard widget display * the html content of a res_widget given as argument */ -openerp.web.client_actions.add( 'board.home.widgets', 'openerp.web_dashboard.Widget'); -openerp.web_dashboard.Widget = openerp.web.View.extend(/** @lends openerp.web_dashboard.Widgets# */{ +instance.web.client_actions.add( 'board.home.widgets', 'instance.web_dashboard.Widget'); +instance.web_dashboard.Widget = instance.web.View.extend(/** @lends instance.web_dashboard.Widgets# */{ template: 'HomeWidget', /** * Initializes a "HomeWidget" client widget: handles the display of a given * res.widget objects in an OpenERP view (mainly a dashboard). * - * @constructs openerp.web_dashboard.Widget - * @extends openerp.web.View + * @constructs instance.web_dashboard.Widget + * @extends instance.web.View * * @param {Object} parent * @param {Object} options @@ -383,8 +382,7 @@ openerp.web_dashboard.Widget = openerp.web.View.extend(/** @lends openerp.web_da this.widget_id = options.widget_id; }, start: function () { - this._super(); - var ds = new openerp.web.DataSet(this, 'res.widget'); + var ds = new instance.web.DataSet(this, 'res.widget'); return ds.read_ids([this.widget_id], ['title']).then(this.on_widget_loaded); }, on_widget_loaded: function (widgets) { @@ -403,8 +401,8 @@ openerp.web_dashboard.Widget = openerp.web.View.extend(/** @lends openerp.web_da * HomeTiles this client action display either the list of application to * install (if none is installed yet) or a list of root menu items */ -openerp.web.client_actions.add('default_home', 'session.web_dashboard.ApplicationTiles'); -openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({ +instance.web.client_actions.add('default_home', 'session.web_dashboard.ApplicationTiles'); +instance.web_dashboard.ApplicationTiles = instance.web.OldWidget.extend({ template: 'web_dashboard.ApplicationTiles', init: function(parent) { this._super(parent); @@ -412,7 +410,7 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({ start: function() { var self = this; var domain = [['application','=',true], ['state','=','installed'], ['name', '!=', 'base']]; - var ds = new openerp.web.DataSetSearch(this, 'ir.module.module',{},domain); + var ds = new instance.web.DataSetSearch(this, 'ir.module.module',{},domain); ds.read_slice(['id']).then(function(result) { if(result.length) { self.on_installed_database(); @@ -422,17 +420,17 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({ }); }, on_uninstalled_database: function() { - installer = new openerp.web_dashboard.ApplicationInstaller(this); + installer = new instance.web_dashboard.ApplicationInstaller(this); installer.appendTo(this.$element); }, on_installed_database: function() { var self = this; self.rpc('/web/menu/get_user_roots', {}).then(function (menu_ids) { - var menuds = new openerp.web.DataSet(this, 'ir.ui.menu',{}) + var menuds = new instance.web.DataSet(this, 'ir.ui.menu',{}) .read_ids(menu_ids, ['name', 'web_icon_data', 'web_icon_hover_data', 'module']).then(function (applications) { var tiles = QWeb.render('ApplicationTiles.content', {applications: applications}); $(tiles).appendTo(self.$element).find('.oe_install-module-link').click(function () { - openerp.webclient.menu.on_menu_click(null, $(this).data('menu')) + instance.webclient.menu.on_menu_click(null, $(this).data('menu')) }); }); }); @@ -443,13 +441,13 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({ * ApplicationInstaller * This client action display a list of applications to install. */ -openerp.web.client_actions.add( 'board.application.installer', 'openerp.web_dashboard.ApplicationInstaller'); -openerp.web_dashboard.ApplicationInstaller = openerp.web.OldWidget.extend({ +instance.web.client_actions.add( 'board.application.installer', 'instance.web_dashboard.ApplicationInstaller'); +instance.web_dashboard.ApplicationInstaller = instance.web.OldWidget.extend({ template: 'web_dashboard.ApplicationInstaller', start: function () { // TODO menu hide var r = this._super(); - this.action_manager = new openerp.web.ActionManager(this); + this.action_manager = new instance.web.ActionManager(this); this.action_manager.appendTo(this.$element.find('.oe_installer')); this.action_manager.do_action({ type: 'ir.actions.act_window', diff --git a/addons/web_diagram/i18n/bs.po b/addons/web_diagram/i18n/bs.po new file mode 100644 index 00000000000..d51c0ca2420 --- /dev/null +++ b/addons/web_diagram/i18n/bs.po @@ -0,0 +1,57 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-16 15:36+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-17 05:24+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:11 +msgid "Diagram" +msgstr "Dijagram" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:224 +#: addons/web_diagram/static/src/js/diagram.js:257 +msgid "Activity" +msgstr "Aktivnost" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:289 +#: addons/web_diagram/static/src/js/diagram.js:308 +msgid "Transition" +msgstr "Prijelaz" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:214 +#: addons/web_diagram/static/src/js/diagram.js:262 +#: addons/web_diagram/static/src/js/diagram.js:314 +msgid "Create:" +msgstr "Kreiraj" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:231 +#: addons/web_diagram/static/src/js/diagram.js:232 +#: addons/web_diagram/static/src/js/diagram.js:296 +msgid "Open: " +msgstr "Otvori: " + +#. openerp-web +#: addons/web_diagram/static/src/xml/base_diagram.xml:5 +#: addons/web_diagram/static/src/xml/base_diagram.xml:6 +msgid "New Node" +msgstr "Novi čvor" diff --git a/addons/web_diagram/i18n/es_CL.po b/addons/web_diagram/i18n/es_CL.po new file mode 100644 index 00000000000..11325dd23d5 --- /dev/null +++ b/addons/web_diagram/i18n/es_CL.po @@ -0,0 +1,57 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-14 15:15+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:11 +msgid "Diagram" +msgstr "Gráfico" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:224 +#: addons/web_diagram/static/src/js/diagram.js:257 +msgid "Activity" +msgstr "Actividad" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:289 +#: addons/web_diagram/static/src/js/diagram.js:308 +msgid "Transition" +msgstr "Transición" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:214 +#: addons/web_diagram/static/src/js/diagram.js:262 +#: addons/web_diagram/static/src/js/diagram.js:314 +msgid "Create:" +msgstr "Crear:" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:231 +#: addons/web_diagram/static/src/js/diagram.js:232 +#: addons/web_diagram/static/src/js/diagram.js:296 +msgid "Open: " +msgstr "Abrir: " + +#. openerp-web +#: addons/web_diagram/static/src/xml/base_diagram.xml:5 +#: addons/web_diagram/static/src/xml/base_diagram.xml:6 +msgid "New Node" +msgstr "Nuevo nodo" diff --git a/addons/web_diagram/i18n/sv.po b/addons/web_diagram/i18n/sv.po new file mode 100644 index 00000000000..da5bb3fbcdd --- /dev/null +++ b/addons/web_diagram/i18n/sv.po @@ -0,0 +1,57 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-17 13:23+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:11 +msgid "Diagram" +msgstr "Diagram" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:224 +#: addons/web_diagram/static/src/js/diagram.js:257 +msgid "Activity" +msgstr "Aktivitet" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:208 +#: addons/web_diagram/static/src/js/diagram.js:289 +#: addons/web_diagram/static/src/js/diagram.js:308 +msgid "Transition" +msgstr "Övergång" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:214 +#: addons/web_diagram/static/src/js/diagram.js:262 +#: addons/web_diagram/static/src/js/diagram.js:314 +msgid "Create:" +msgstr "Skapa:" + +#. openerp-web +#: addons/web_diagram/static/src/js/diagram.js:231 +#: addons/web_diagram/static/src/js/diagram.js:232 +#: addons/web_diagram/static/src/js/diagram.js:296 +msgid "Open: " +msgstr "Öppna: " + +#. openerp-web +#: addons/web_diagram/static/src/xml/base_diagram.xml:5 +#: addons/web_diagram/static/src/xml/base_diagram.xml:6 +msgid "New Node" +msgstr "Ny nod" diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 41b3c4844db..ddbd385cb84 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -2,12 +2,12 @@ * OpenERP diagram library *---------------------------------------------------------*/ -openerp.web_diagram = function (openerp) { -var QWeb = openerp.web.qweb, - _t = openerp.web._t, - _lt = openerp.web._lt; -openerp.web.views.add('diagram', 'openerp.web.DiagramView'); -openerp.web.DiagramView = openerp.web.View.extend({ +openerp.web_diagram = function (instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t, + _lt = instance.web._lt; +instance.web.views.add('diagram', 'instance.web.DiagramView'); +instance.web.DiagramView = instance.web.View.extend({ display_name: _lt('Diagram'), searchable: false, init: function(parent, dataset, view_id, options) { @@ -22,7 +22,6 @@ openerp.web.DiagramView = openerp.web.View.extend({ this.ids = this.dataset.ids; }, start: function() { - this._super(); return this.rpc("/web_diagram/diagram/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); }, @@ -195,7 +194,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ if(!confirm(_t("Deleting this node cannot be undone.\nIt will also delete all connected transitions.\n\nAre you sure ?"))){ return $.Deferred().reject().promise(); } - return new openerp.web.DataSet(self,self.node).unlink([cutenode.id]); + return new instance.web.DataSet(self,self.node).unlink([cutenode.id]); }; CuteEdge.double_click_callback = function(cuteedge){ self.edit_connector(cuteedge.id); @@ -213,7 +212,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ if(!confirm(_t("Deleting this transition cannot be undone.\n\nAre you sure ?"))){ return $.Deferred().reject().promise(); } - return new openerp.web.DataSet(self,self.connector).unlink([cuteedge.id]); + return new instance.web.DataSet(self,self.connector).unlink([cuteedge.id]); }; }, @@ -222,7 +221,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ edit_node: function(node_id){ var self = this; var title = _t('Activity'); - var pop = new openerp.web.form.FormOpenPopup(self); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element( self.node, @@ -255,7 +254,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ add_node: function(){ var self = this; var title = _t('Activity'); - var pop = new openerp.web.form.SelectCreatePopup(self); + var pop = new instance.web.form.SelectCreatePopup(self); pop.select_element( self.node, { @@ -287,7 +286,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ edit_connector: function(connector_id){ var self = this; var title = _t('Transition'); - var pop = new openerp.web.form.FormOpenPopup(self); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element( self.connector, parseInt(connector_id,10), //FIXME Isn't connector_id supposed to be an int ? @@ -306,7 +305,7 @@ openerp.web.DiagramView = openerp.web.View.extend({ add_connector: function(node_source_id, node_dest_id, dummy_cuteedge){ var self = this; var title = _t('Transition'); - var pop = new openerp.web.form.SelectCreatePopup(self); + var pop = new instance.web.form.SelectCreatePopup(self); pop.select_element( self.connector, diff --git a/addons/web_gantt/i18n/bn.po b/addons/web_gantt/i18n/bn.po new file mode 100644 index 00000000000..2e5985e82f3 --- /dev/null +++ b/addons/web_gantt/i18n/bn.po @@ -0,0 +1,28 @@ +# Bengali translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-12 14:53+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bengali <bn@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-04-13 05:38+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_gantt/static/src/js/gantt.js:11 +msgid "Gantt" +msgstr "জ্ঞান্ট" + +#. openerp-web +#: addons/web_gantt/static/src/xml/web_gantt.xml:10 +msgid "Create" +msgstr "তৈরি করুন" diff --git a/addons/web_gantt/i18n/bs.po b/addons/web_gantt/i18n/bs.po new file mode 100644 index 00000000000..2e6f985dfaf --- /dev/null +++ b/addons/web_gantt/i18n/bs.po @@ -0,0 +1,28 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-16 15:37+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-17 05:24+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_gantt/static/src/js/gantt.js:11 +msgid "Gantt" +msgstr "Gant" + +#. openerp-web +#: addons/web_gantt/static/src/xml/web_gantt.xml:10 +msgid "Create" +msgstr "Kreiraj" diff --git a/addons/web_gantt/i18n/en_AU.po b/addons/web_gantt/i18n/en_AU.po new file mode 100644 index 00000000000..224b1fe7d1e --- /dev/null +++ b/addons/web_gantt/i18n/en_AU.po @@ -0,0 +1,28 @@ +# English (Australia) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-09 03:28+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: English (Australia) <en_AU@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-04-10 05:02+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_gantt/static/src/js/gantt.js:11 +msgid "Gantt" +msgstr "Gantt" + +#. openerp-web +#: addons/web_gantt/static/src/xml/web_gantt.xml:10 +msgid "Create" +msgstr "Create" diff --git a/addons/web_gantt/i18n/es_CL.po b/addons/web_gantt/i18n/es_CL.po new file mode 100644 index 00000000000..2c60e6b370d --- /dev/null +++ b/addons/web_gantt/i18n/es_CL.po @@ -0,0 +1,28 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-14 15:15+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_gantt/static/src/js/gantt.js:11 +msgid "Gantt" +msgstr "Gantt" + +#. openerp-web +#: addons/web_gantt/static/src/xml/web_gantt.xml:10 +msgid "Create" +msgstr "Crear" diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js index 9f49ba7bd5e..5643e0cefb3 100644 --- a/addons/web_gantt/static/src/js/gantt.js +++ b/addons/web_gantt/static/src/js/gantt.js @@ -1,29 +1,28 @@ /*--------------------------------------------------------- * OpenERP web_gantt *---------------------------------------------------------*/ -openerp.web_gantt = function (openerp) { -var _t = openerp.web._t, - _lt = openerp.web._lt; -var QWeb = openerp.web.qweb; -openerp.web.views.add('gantt', 'openerp.web_gantt.GanttView'); +openerp.web_gantt = function (instance) { +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; +instance.web.views.add('gantt', 'instance.web_gantt.GanttView'); -openerp.web_gantt.GanttView = openerp.web.View.extend({ +instance.web_gantt.GanttView = instance.web.View.extend({ display_name: _lt('Gantt'), template: "GanttView", + view_type: "gantt", init: function() { this._super.apply(this, arguments); this.has_been_loaded = $.Deferred(); this.chart_id = _.uniqueId(); }, - start: function() { - return $.when(this.rpc("/web/view/load", {"model": this.dataset.model, "view_id": this.view_id, "view_type": "gantt"}), - this.rpc("/web/searchview/fields_get", {"model": this.dataset.model})).pipe(this.on_loaded); - }, - on_loaded: function(fields_view, fields_get) { - this.fields_view = fields_view[0]; - this.fields = fields_get[0].fields; - - this.has_been_loaded.resolve(); + on_loaded: function(fields_view_get, fields_get) { + var self = this; + this.fields_view = fields_view_get; + return this.rpc("/web/searchview/fields_get", {"model": this.dataset.model}).pipe(function(fields_get) { + self.fields = fields_get.fields; + self.has_been_loaded.resolve(); + }); }, do_search: function (domains, contexts, group_bys) { var self = this; @@ -123,7 +122,7 @@ openerp.web_gantt.GanttView = openerp.web.View.extend({ return memo === undefined || date > memo ? date : memo; }, undefined); var duration = (task_stop.getTime() - task_start.getTime()) / (1000 * 60 * 60); - var group_name = openerp.web.format_value(task.name, self.fields[group_bys[level]]); + var group_name = instance.web.format_value(task.name, self.fields[group_bys[level]]); if (level == 0) { var group = new GanttProjectInfo(_.uniqueId("gantt_project_"), group_name, task_start); _.each(task_infos, function(el) { @@ -139,16 +138,16 @@ openerp.web_gantt.GanttView = openerp.web.View.extend({ } } else { var task_name = task.__name; - var task_start = openerp.web.auto_str_to_date(task[self.fields_view.arch.attrs.date_start]); + var task_start = instance.web.auto_str_to_date(task[self.fields_view.arch.attrs.date_start]); if (!task_start) return; var task_stop; if (self.fields_view.arch.attrs.date_stop) { - task_stop = openerp.web.auto_str_to_date(task[self.fields_view.arch.attrs.date_stop]); + task_stop = instance.web.auto_str_to_date(task[self.fields_view.arch.attrs.date_stop]); if (!task_stop) return; } else { // we assume date_duration is defined - var tmp = openerp.web.format_value(task[self.fields_view.arch.attrs.date_delay], + var tmp = instance.web.format_value(task[self.fields_view.arch.attrs.date_delay], self.fields[self.fields_view.arch.attrs.date_delay]); if (!tmp) return; @@ -198,10 +197,10 @@ openerp.web_gantt.GanttView = openerp.web.View.extend({ var end = start.clone().addMilliseconds(duration * 60 * 60 * 1000); var data = {}; data[self.fields_view.arch.attrs.date_start] = - openerp.web.auto_date_to_str(start, self.fields[self.fields_view.arch.attrs.date_start].type); + instance.web.auto_date_to_str(start, self.fields[self.fields_view.arch.attrs.date_start].type); if (self.fields_view.arch.attrs.date_stop) { data[self.fields_view.arch.attrs.date_stop] = - openerp.web.auto_date_to_str(end, self.fields[self.fields_view.arch.attrs.date_stop].type); + instance.web.auto_date_to_str(end, self.fields[self.fields_view.arch.attrs.date_stop].type); } else { // we assume date_duration is defined data[self.fields_view.arch.attrs.date_delay] = duration; } @@ -211,7 +210,7 @@ openerp.web_gantt.GanttView = openerp.web.View.extend({ }, on_task_display: function(task) { var self = this; - var pop = new openerp.web.form.FormOpenPopup(self); + var pop = new instance.web.form.FormOpenPopup(self); pop.on_write_completed.add_last(function() { self.reload(); }); @@ -224,7 +223,7 @@ openerp.web_gantt.GanttView = openerp.web.View.extend({ }, on_task_create: function() { var self = this; - var pop = new openerp.web.form.SelectCreatePopup(this); + var pop = new instance.web.form.SelectCreatePopup(this); pop.on_select_elements.add_last(function() { self.reload(); }); diff --git a/addons/web_graph/i18n/bn.po b/addons/web_graph/i18n/bn.po new file mode 100644 index 00000000000..e82aa395906 --- /dev/null +++ b/addons/web_graph/i18n/bn.po @@ -0,0 +1,23 @@ +# Bengali translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-12 14:54+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bengali <bn@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-04-13 05:38+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_graph/static/src/js/graph.js:19 +msgid "Graph" +msgstr "লেখচিত্র" diff --git a/addons/web_graph/i18n/bs.po b/addons/web_graph/i18n/bs.po new file mode 100644 index 00000000000..d8ca1b91203 --- /dev/null +++ b/addons/web_graph/i18n/bs.po @@ -0,0 +1,23 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-16 15:37+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-17 05:24+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_graph/static/src/js/graph.js:19 +msgid "Graph" +msgstr "Graf" diff --git a/addons/web_graph/i18n/en_AU.po b/addons/web_graph/i18n/en_AU.po new file mode 100644 index 00000000000..4ddfd3c2589 --- /dev/null +++ b/addons/web_graph/i18n/en_AU.po @@ -0,0 +1,23 @@ +# English (Australia) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-09 03:27+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: English (Australia) <en_AU@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-04-10 05:02+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_graph/static/src/js/graph.js:19 +msgid "Graph" +msgstr "Graph" diff --git a/addons/web_graph/i18n/es_CL.po b/addons/web_graph/i18n/es_CL.po new file mode 100644 index 00000000000..ef89f84940e --- /dev/null +++ b/addons/web_graph/i18n/es_CL.po @@ -0,0 +1,23 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-14 15:16+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_graph/static/src/js/graph.js:19 +msgid "Graph" +msgstr "Gráfico" diff --git a/addons/web_graph/i18n/sv.po b/addons/web_graph/i18n/sv.po new file mode 100644 index 00000000000..98335a89577 --- /dev/null +++ b/addons/web_graph/i18n/sv.po @@ -0,0 +1,23 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-06 17:33+0100\n" +"PO-Revision-Date: 2012-04-17 13:21+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_graph/static/src/js/graph.js:19 +msgid "Graph" +msgstr "Diagram" diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index d962c7d6752..7016b9d8d85 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -2,7 +2,7 @@ * OpenERP web_graph *---------------------------------------------------------*/ -openerp.web_graph = function (openerp) { +openerp.web_graph = function (instance) { var COLOR_PALETTE = [ '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b', '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00', @@ -12,11 +12,12 @@ var COLOR_PALETTE = [ '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900', '#cc0000', '#d400a8']; -var QWeb = openerp.web.qweb, - _lt = openerp.web._lt; -openerp.web.views.add('graph', 'openerp.web_graph.GraphView'); -openerp.web_graph.GraphView = openerp.web.View.extend({ +var QWeb = instance.web.qweb, + _lt = instance.web._lt; +instance.web.views.add('graph', 'instance.web_graph.GraphView'); +instance.web_graph.GraphView = instance.web.View.extend({ display_name: _lt('Graph'), + view_type: "graph", init: function(parent, dataset, view_id, options) { this._super(parent); @@ -39,27 +40,13 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ } this._super(); }, - start: function() { + on_loaded: function(fields_view_get) { var self = this; - this._super(); - var loaded; - if (this.embedded_view) { - loaded = $.when([self.embedded_view]); - } else { - loaded = this.rpc('/web/view/load', { - model: this.dataset.model, - view_id: this.view_id, - view_type: 'graph' - }); - } - return $.when( - this.dataset.call_and_eval('fields_get', [false, {}], null, 1), - loaded) - .then(function (fields_result, view_result) { - self.fields = fields_result[0]; - self.fields_view = view_result[0]; - self.on_loaded(self.fields_view); - }); + self.fields_view = fields_view_get; + return this.dataset.call_and_eval('fields_get', [false, {}], null, 1).pipe(function(fields_result) { + self.fields = fields_result; + return self.on_loaded_2(); + }); }, /** * Returns all object fields involved in the graph view @@ -72,7 +59,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ } return fs; }, - on_loaded: function() { + on_loaded_2: function() { this.chart = this.fields_view.arch.attrs.type || 'pie'; this.orientation = this.fields_view.arch.attrs.orientation || 'vertical'; @@ -91,6 +78,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ }, this); this.ordinate = this.columns[0].name; this.is_loaded.resolve(); + return $.when(); }, schedule_chart: function(results) { var self = this; diff --git a/addons/web_hello/static/openerp/base_hello.js b/addons/web_hello/static/openerp/base_hello.js index 91caf263196..a74b8ef8f2f 100644 --- a/addons/web_hello/static/openerp/base_hello.js +++ b/addons/web_hello/static/openerp/base_hello.js @@ -2,9 +2,9 @@ * OpenERP base_hello (Example module) *---------------------------------------------------------*/ -openerp.web_hello = function(openerp) { +openerp.web_hello = function(instance) { -openerp.web.SearchView = openerp.web.SearchView.extend({ +instance.web.SearchView = instance.web.SearchView.extend({ init:function() { this._super.apply(this,arguments); this.on_search.add(function(){console.log('hello');}); @@ -13,7 +13,7 @@ openerp.web.SearchView = openerp.web.SearchView.extend({ // here you may tweak globals object, if any, and play with on_* or do_* callbacks on them -openerp.web.Login = openerp.web.Login.extend({ +instance.web.Login = instance.web.Login.extend({ start: function() { console.log('Hello there'); this._super.apply(this,arguments); diff --git a/addons/web_kanban/i18n/bn.po b/addons/web_kanban/i18n/bn.po new file mode 100644 index 00000000000..07836239dfe --- /dev/null +++ b/addons/web_kanban/i18n/bn.po @@ -0,0 +1,55 @@ +# Bengali translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-12 15:08+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bengali <bn@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-04-13 05:38+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:10 +msgid "Kanban" +msgstr "কানবান" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:294 +#: addons/web_kanban/static/src/js/kanban.js:295 +msgid "Undefined" +msgstr "অনির্ণীত" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:469 +#: addons/web_kanban/static/src/js/kanban.js:470 +msgid "Are you sure you want to delete this record ?" +msgstr "আপনি কি নিশ্চিত যে আপনি এই তথ্য মুছে ফেলতে চান?" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:5 +msgid "Create" +msgstr "তৈরি করুন" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "Show more... (" +msgstr "আরও দেখান...(" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "remaining)" +msgstr "অবশিষ্ট)" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:59 +msgid "</tr><tr>" +msgstr "</tr><tr>" diff --git a/addons/web_kanban/i18n/bs.po b/addons/web_kanban/i18n/bs.po new file mode 100644 index 00000000000..b2c55c8d618 --- /dev/null +++ b/addons/web_kanban/i18n/bs.po @@ -0,0 +1,55 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-16 15:39+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-17 05:24+0000\n" +"X-Generator: Launchpad (build 15099)\n" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:10 +msgid "Kanban" +msgstr "" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:294 +#: addons/web_kanban/static/src/js/kanban.js:295 +msgid "Undefined" +msgstr "Nedefinisano" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:469 +#: addons/web_kanban/static/src/js/kanban.js:470 +msgid "Are you sure you want to delete this record ?" +msgstr "Jeste li sigurni da želite izbrisati zapis?" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:5 +msgid "Create" +msgstr "Kreiraj" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "Show more... (" +msgstr "Prikaži više" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "remaining)" +msgstr "Preostalo" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:59 +msgid "</tr><tr>" +msgstr "</tr><tr>" diff --git a/addons/web_kanban/i18n/es_CL.po b/addons/web_kanban/i18n/es_CL.po new file mode 100644 index 00000000000..967411e509b --- /dev/null +++ b/addons/web_kanban/i18n/es_CL.po @@ -0,0 +1,55 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-14 15:16+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:10 +msgid "Kanban" +msgstr "Kanban" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:294 +#: addons/web_kanban/static/src/js/kanban.js:295 +msgid "Undefined" +msgstr "Sin definir" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:469 +#: addons/web_kanban/static/src/js/kanban.js:470 +msgid "Are you sure you want to delete this record ?" +msgstr "¿Está seguro que quiere eliminar este registro?" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:5 +msgid "Create" +msgstr "Crear" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "Show more... (" +msgstr "Mostrar más... (" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "remaining)" +msgstr "restante)" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:59 +msgid "</tr><tr>" +msgstr "</tr><tr>" diff --git a/addons/web_kanban/i18n/sv.po b/addons/web_kanban/i18n/sv.po new file mode 100644 index 00000000000..ad96ac598d2 --- /dev/null +++ b/addons/web_kanban/i18n/sv.po @@ -0,0 +1,55 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-14 15:27+0100\n" +"PO-Revision-Date: 2012-04-17 12:50+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:10 +msgid "Kanban" +msgstr "Kanban" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:294 +#: addons/web_kanban/static/src/js/kanban.js:295 +msgid "Undefined" +msgstr "Odefinierad" + +#. openerp-web +#: addons/web_kanban/static/src/js/kanban.js:469 +#: addons/web_kanban/static/src/js/kanban.js:470 +msgid "Are you sure you want to delete this record ?" +msgstr "Är du säker på att du vill radera denna post?" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:5 +msgid "Create" +msgstr "Skapa" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "Show more... (" +msgstr "Visa mer... (" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:41 +msgid "remaining)" +msgstr "återstående)" + +#. openerp-web +#: addons/web_kanban/static/src/xml/web_kanban.xml:59 +msgid "</tr><tr>" +msgstr "</tr><tr>" diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index aecd5eb036e..d23174df74c 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -3,6 +3,9 @@ visibility: visible !important; height: 60px !important; } +.openerp .oe_kanban_groups { + width: 100%; +} .openerp .oe_kanban_group_header { position: relative; } diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 2993954e3aa..d3ba0c74f5a 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -1,14 +1,15 @@ -openerp.web_kanban = function (openerp) { +openerp.web_kanban = function (instance) { -var _t = openerp.web._t, - _lt = openerp.web._lt; -var QWeb = openerp.web.qweb; -openerp.web.views.add('kanban', 'openerp.web_kanban.KanbanView'); +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; +instance.web.views.add('kanban', 'instance.web_kanban.KanbanView'); -openerp.web_kanban.KanbanView = openerp.web.View.extend({ +instance.web_kanban.KanbanView = instance.web.View.extend({ template: "KanbanView", display_name: _lt('Kanban'), default_nr_columns: 3, + view_type: "kanban", init: function (parent, dataset, view_id, options) { this._super(parent); this.set_default_options(options); @@ -22,42 +23,41 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ records : {} }; this.groups = []; - this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); + this.form_dialog = new instance.web.form.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.do_reload); this.aggregates = {}; this.group_operators = ['avg', 'max', 'min', 'sum', 'count']; this.qweb = new QWeb2.Engine(); - this.qweb.debug = openerp.connection.debug; + this.qweb.debug = instance.connection.debug; this.qweb.default_dict = _.clone(QWeb.default_dict); this.has_been_loaded = $.Deferred(); this.search_domain = this.search_context = this.search_group_by = null; this.currently_dragging = {}; this.limit = options.limit || 80; - }, - start: function() { - this._super(); - this.$element.find('button.oe_kanban_button_new').click(this.do_add_record); - this.$groups = this.$element.find('.oe_kanban_groups tr'); - var context = new openerp.web.CompoundContext(this.dataset.get_context()); - return this.rpc('/web/view/load', { - 'model': this.dataset.model, - 'view_id': this.view_id, - 'view_type': 'kanban', - context: context - }, this.on_loaded); + this.add_group_mutex = new $.Mutex(); }, on_loaded: function(data) { + this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this})); + if (this.options.$buttons) { + this.$buttons.appendTo(this.options.$buttons); + } else { + this.$element.find('.oe_kanban_buttons').replaceWith(this.$buttons); + } + this.$buttons + .on('click','button.oe_kanban_button_new', this.do_add_record); + this.$groups = this.$element.find('.oe_kanban_groups tr'); this.fields_view = data; this.fields_keys = _.keys(this.fields_view.fields); this.add_qweb_template(); this.has_been_loaded.resolve(); + return $.when(); }, add_qweb_template: function() { for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) { var child = this.fields_view.arch.children[i]; if (child.tag === "templates") { this.transform_qweb_template(child); - this.qweb.add_template(openerp.web.json_node_to_xml(child)); + this.qweb.add_template(instance.web.json_node_to_xml(child)); break; } else if (child.tag === 'field') { this.extract_aggregates(child); @@ -109,7 +109,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ node.children = [{ tag: 'img', attrs: { - src: openerp.connection.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png', + src: instance.connection.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png', width: '16', height: '16' } @@ -141,34 +141,48 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.search_group_by = group_by; $.when(this.has_been_loaded).then(function() { self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by; - self.datagroup = new openerp.web.DataGroup(self, self.dataset.model, domain, context, self.group_by ? [self.group_by] : []); + self.datagroup = new instance.web.DataGroup(self, self.dataset.model, domain, context, self.group_by ? [self.group_by] : []); self.datagroup.list(self.fields_keys, self.do_process_groups, self.do_process_dataset); }); }, do_process_groups: function(groups) { - this.do_clear_groups(); - this.dataset.ids = []; - var self = this, - remaining = groups.length - 1, - groups_array = []; - _.each(groups, function (group, index) { - var dataset = new openerp.web.DataSetSearch(self, self.dataset.model, group.context, group.domain); - dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) { - self.dataset.ids.push.apply(self.dataset.ids, dataset.ids); - groups_array[index] = new openerp.web_kanban.KanbanGroup(self, records, group, dataset); - if (!remaining--) { - self.dataset.index = self.dataset.size() ? 0 : null; - self.do_add_groups(groups_array); - } + var self = this; + this.add_group_mutex.exec(function() { + var def = $.Deferred(); + self.do_clear_groups(); + self.dataset.ids = []; + var remaining = groups.length - 1, + groups_array = []; + _.each(groups, function (group, index) { + var dataset = new instance.web.DataSetSearch(self, self.dataset.model, group.context, group.domain); + dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) { + self.dataset.ids.push.apply(self.dataset.ids, dataset.ids); + groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset); + if (!remaining--) { + self.dataset.index = self.dataset.size() ? 0 : null; + def.pipe(self.do_add_groups(groups_array)); + } + }).then(null, function() { + def.reject(); + }); }); + return def; }); }, do_process_dataset: function(dataset) { var self = this; - this.do_clear_groups(); - this.dataset.read_slice(this.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) { - var kgroup = new openerp.web_kanban.KanbanGroup(self, records, null, self.dataset); - self.do_add_groups([kgroup]); + this.add_group_mutex.exec(function() { + var def = $.Deferred(); + self.do_clear_groups(); + self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) { + var kgroup = new instance.web_kanban.KanbanGroup(self, records, null, self.dataset); + self.do_add_groups([kgroup]).then(function() { + def.resolve(); + }); + }).then(null, function() { + def.reject(); + }); + return def; }); }, do_reload: function() { @@ -182,14 +196,16 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.$element.find('.oe_kanban_groups_headers, .oe_kanban_groups_records').empty(); }, do_add_groups: function(groups) { - var self = this; + var self = this, + def = $.Deferred().resolve(); _.each(groups, function(group) { self.groups[group.undefined_title ? 'unshift' : 'push'](group); }); _.each(this.groups, function(group) { - group.appendTo(self.$element.find('.oe_kanban_groups_headers')); + def.pipe(group.appendTo(self.$element.find('.oe_kanban_groups_headers'))); }); this.on_groups_started(); + return def; }, on_groups_started: function() { var self = this; @@ -256,12 +272,28 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, do_show: function() { + if (this.$buttons) { + this.$buttons.show(); + } this.do_push_state({}); return this._super(); - } + }, + do_hide: function () { + if (this.$buttons) { + this.$buttons.hide(); + } + return this._super(); + }, + open_record: function(id) { + if (this.dataset.select_id(id)) { + this.do_switch_view('form'); + } else { + this.do_warn("Kanban: could not find id#" + id); + } + }, }); -openerp.web_kanban.KanbanGroup = openerp.web.OldWidget.extend({ +instance.web_kanban.KanbanGroup = instance.web.OldWidget.extend({ template: 'KanbanView.group_header', init: function (parent, records, group, dataset) { var self = this; @@ -283,7 +315,7 @@ openerp.web_kanban.KanbanGroup = openerp.web.OldWidget.extend({ var field = this.view.fields_view.fields[this.view.group_by]; if (field) { try { - this.title = openerp.web.format_value(group.value, field, false); + this.title = instance.web.format_value(group.value, field, false); } catch(e) {} } _.each(this.view.aggregates, function(value, key) { @@ -344,7 +376,7 @@ openerp.web_kanban.KanbanGroup = openerp.web.OldWidget.extend({ do_add_records: function(records) { var self = this; _.each(records, function(record) { - var rec = new openerp.web_kanban.KanbanRecord(self, record); + var rec = new instance.web_kanban.KanbanRecord(self, record); rec.insertBefore(self.$records.find('.oe_kanban_show_more')); self.records.push(rec); }); @@ -372,7 +404,7 @@ openerp.web_kanban.KanbanGroup = openerp.web.OldWidget.extend({ } }); -openerp.web_kanban.KanbanRecord = openerp.web.OldWidget.extend({ +instance.web_kanban.KanbanRecord = instance.web.OldWidget.extend({ template: 'KanbanView.record', init: function (parent, record) { this._super(parent); @@ -402,11 +434,11 @@ openerp.web_kanban.KanbanRecord = openerp.web.OldWidget.extend({ _.each(record, function(value, name) { var r = _.clone(self.view.fields_view.fields[name] || {}); if ((r.type === 'date' || r.type === 'datetime') && value) { - r.raw_value = openerp.web.auto_str_to_date(value); + r.raw_value = instance.web.auto_str_to_date(value); } else { r.raw_value = value; } - r.value = openerp.web.format_value(value, r); + r.value = instance.web.format_value(value, r); new_record[name] = r; }); return new_record; @@ -481,11 +513,7 @@ openerp.web_kanban.KanbanRecord = openerp.web.OldWidget.extend({ self.view.form_dialog.open(); }); } else { - if (self.view.dataset.select_id(this.id)) { - this.view.do_switch_view('form'); - } else { - this.do_warn("Kanban: could not find id#" + id); - } + this.view.open_record(this.id); } }, do_action_color: function($action) { @@ -556,9 +584,9 @@ openerp.web_kanban.KanbanRecord = openerp.web.OldWidget.extend({ }, kanban_image: function(model, field, id) { id = id || ''; - var url = openerp.connection.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id; + var url = instance.connection.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id; if (this.record.__last_update && this.record.__last_update.raw_value) { - var time = openerp.web.str_to_datetime(this.record.__last_update.raw_value).getTime(); + var time = instance.web.str_to_datetime(this.record.__last_update.raw_value).getTime(); url += '&t=' + time; } return url; diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index bee778965ae..0238c7ac357 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -1,12 +1,8 @@ <template> <t t-name="KanbanView"> - <div class="oe_kanban_header"> - <t t-if="widget.options.action_buttons !== false"> - <button type="button" class="oe_button oe_kanban_button_new">Create</button> - </t> - </div> + <div class="oe_kanban_buttons"/> <div class="oe_kanban_view"> - <table style="width:100%;" class="oe_kanban_groups"> + <table class="oe_kanban_groups"> <tr class="oe_kanban_groups_headers"> </tr> <tr class="oe_kanban_groups_records"> @@ -14,6 +10,11 @@ </table> </div> </t> +<div t-name="KanbanView.buttons" class="oe_kanban_buttons"> + <t t-if="widget.options.action_buttons !== false"> + <button type="button" class="oe_button oe_kanban_button_new">Create</button> + </t> +</div> <t t-name="KanbanView.group_header"> <td> <t t-if="widget.view.group_by"> diff --git a/addons/web_mobile/i18n/bn.po b/addons/web_mobile/i18n/bn.po index 031f5e05dd0..1a31822df52 100644 --- a/addons/web_mobile/i18n/bn.po +++ b/addons/web_mobile/i18n/bn.po @@ -8,59 +8,59 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-07 10:13+0100\n" -"PO-Revision-Date: 2011-11-24 12:56+0000\n" -"Last-Translator: nasir khan saikat <nasir8891@gmail.com>\n" +"PO-Revision-Date: 2012-04-14 22:49+0000\n" +"Last-Translator: Kazi Shahnoor Ashraf <kazidxb@gmail.com>\n" "Language-Team: Bengali <bn@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-02-08 06:39+0000\n" -"X-Generator: Launchpad (build 14747)\n" +"X-Launchpad-Export-Date: 2012-04-16 04:50+0000\n" +"X-Generator: Launchpad (build 15099)\n" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:17 msgid "OpenERP" -msgstr "" +msgstr "ওপেন-ই-আর-পি" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:22 msgid "Database:" -msgstr "ডেটাবেস:" +msgstr "উপাত্তুৎস" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:30 msgid "Login:" -msgstr "লগইন:" +msgstr "প্রবেশ করুন:" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:32 msgid "Password:" -msgstr "পাসওয়ার্ড:" +msgstr "শব্দচাবি:" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:34 msgid "Login" -msgstr "লগইন" +msgstr "প্রবেশ করুন" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:36 msgid "Bad username or password" -msgstr "" +msgstr "ব্যবহারকারীর নাম অথবা পাসওয়ার্ড এ ভূল আছে" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:42 msgid "Powered by openerp.com" -msgstr "" +msgstr "openerp.com এর কর্তৃত্বে" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:49 msgid "Home" -msgstr "" +msgstr "ঘর" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:57 msgid "Favourite" -msgstr "" +msgstr "প্রিয়" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:58 @@ -75,32 +75,32 @@ msgstr "লগআউট" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:132 msgid "There are no records to show." -msgstr "" +msgstr "তথ্য প্রদর্শনের জন্য কিছু নাই" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:183 msgid "Open this resource" -msgstr "" +msgstr "এই তথ্যসম্পদ খুলুন" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:223 #: addons/web_mobile/static/src/xml/web_mobile.xml:226 msgid "Percent of tasks closed according to total of tasks to do..." -msgstr "" +msgstr "সর্বমোট করনীয় কাজের শতাংশ কাজ বন্ধ হয়েছে" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:264 #: addons/web_mobile/static/src/xml/web_mobile.xml:268 msgid "On" -msgstr "" +msgstr "চালু" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:265 #: addons/web_mobile/static/src/xml/web_mobile.xml:269 msgid "Off" -msgstr "" +msgstr "বন্ধ" #. openerp-web #: addons/web_mobile/static/src/xml/web_mobile.xml:294 msgid "Form View" -msgstr "" +msgstr "গাঠনিক দৃশ্য" diff --git a/addons/web_mobile/i18n/es_CL.po b/addons/web_mobile/i18n/es_CL.po new file mode 100644 index 00000000000..42b63f35c83 --- /dev/null +++ b/addons/web_mobile/i18n/es_CL.po @@ -0,0 +1,108 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 10:13+0100\n" +"PO-Revision-Date: 2012-04-14 15:07+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:17 +msgid "OpenERP" +msgstr "OpenERP" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:22 +msgid "Database:" +msgstr "Base de datos:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:30 +msgid "Login:" +msgstr "Inicio de Sesión:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:32 +msgid "Password:" +msgstr "Contraseña:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:34 +msgid "Login" +msgstr "Inicio de sesión" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:36 +msgid "Bad username or password" +msgstr "Nombre de usuario o contraseña incorrectos" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:42 +msgid "Powered by openerp.com" +msgstr "Desarrollado por openerp.com" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:49 +msgid "Home" +msgstr "Inicio" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:57 +msgid "Favourite" +msgstr "Favorito" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:58 +msgid "Preference" +msgstr "Preferencias" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:123 +msgid "Logout" +msgstr "Cerrar sesión" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:132 +msgid "There are no records to show." +msgstr "No hay registros para mostrar." + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:183 +msgid "Open this resource" +msgstr "Abrir este recurso" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:223 +#: addons/web_mobile/static/src/xml/web_mobile.xml:226 +msgid "Percent of tasks closed according to total of tasks to do..." +msgstr "" +"Porcentaje de la tarea realizado de acuerdo al total de la tarea a " +"realizar..." + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:264 +#: addons/web_mobile/static/src/xml/web_mobile.xml:268 +msgid "On" +msgstr "Activo" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:265 +#: addons/web_mobile/static/src/xml/web_mobile.xml:269 +msgid "Off" +msgstr "Apagar" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:294 +msgid "Form View" +msgstr "Vista formulario" diff --git a/addons/web_mobile/i18n/sv.po b/addons/web_mobile/i18n/sv.po new file mode 100644 index 00000000000..d0dc990c7c8 --- /dev/null +++ b/addons/web_mobile/i18n/sv.po @@ -0,0 +1,106 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 10:13+0100\n" +"PO-Revision-Date: 2012-04-17 12:46+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:17 +msgid "OpenERP" +msgstr "OpenERP" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:22 +msgid "Database:" +msgstr "Databas:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:30 +msgid "Login:" +msgstr "Användarnamn:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:32 +msgid "Password:" +msgstr "Lösenord:" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:34 +msgid "Login" +msgstr "Logga in" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:36 +msgid "Bad username or password" +msgstr "Fel användanamn eller lösenord" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:42 +msgid "Powered by openerp.com" +msgstr "Baserad på openerp.com" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:49 +msgid "Home" +msgstr "Hem" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:57 +msgid "Favourite" +msgstr "Favorit" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:58 +msgid "Preference" +msgstr "Inställning" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:123 +msgid "Logout" +msgstr "Logga ut" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:132 +msgid "There are no records to show." +msgstr "Det finns inte några poster att visa." + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:183 +msgid "Open this resource" +msgstr "Öppna denna resurs" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:223 +#: addons/web_mobile/static/src/xml/web_mobile.xml:226 +msgid "Percent of tasks closed according to total of tasks to do..." +msgstr "Andel avslutade uppgifter i jämförelse med alla..." + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:264 +#: addons/web_mobile/static/src/xml/web_mobile.xml:268 +msgid "On" +msgstr "På" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:265 +#: addons/web_mobile/static/src/xml/web_mobile.xml:269 +msgid "Off" +msgstr "Av" + +#. openerp-web +#: addons/web_mobile/static/src/xml/web_mobile.xml:294 +msgid "Form View" +msgstr "Formulär" diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index a28956a0cab..a507e3b5943 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -2,36 +2,36 @@ * OpenERP Web Mobile chrome *---------------------------------------------------------*/ -openerp.web_mobile.chrome_mobile = function(openerp) { +openerp.web_mobile.chrome_mobile = function(instance) { -openerp.web_mobile.mobilewebclient = function(element_id) { - // TODO Helper to start mobile webclient rename it openerp.web.webclient - var client = new openerp.web_mobile.MobileWebClient(element_id); +instance.web_mobile.mobilewebclient = function(element_id) { + // TODO Helper to start mobile webclient rename it instance.web.webclient + var client = new instance.web_mobile.MobileWebClient(element_id); client.start(); return client; }; -openerp.web_mobile.MobileWebClient = openerp.web.OldWidget.extend({ +instance.web_mobile.MobileWebClient = instance.web.OldWidget.extend({ template: "WebClient", init: function(element_id) { this._super(null, element_id); - this.crashmanager = new openerp.web.CrashManager(this); - this.login = new openerp.web_mobile.Login(this, "oe_login"); + this.crashmanager = new instance.web.CrashManager(this); + this.login = new instance.web_mobile.Login(this, "oe_login"); }, start: function() { this._super.apply(this, arguments); var self = this; this.session.bind_session().then(function() { - openerp.web.qweb.add_template("xml/web_mobile.xml"); + instance.web.qweb.add_template("xml/web_mobile.xml"); self.$element.html(self.render()); self.login.start(); }); } }); -openerp.web_mobile.Login = openerp.web.OldWidget.extend({ +instance.web_mobile.Login = instance.web.OldWidget.extend({ template: "Login", @@ -103,7 +103,7 @@ openerp.web_mobile.Login = openerp.web.OldWidget.extend({ .addClass("login_valid"); //.hide(); if(!$('#oe_menu').html().length){ - this.menu = new openerp.web_mobile.Menu(this, "oe_menu", "oe_secondary_menu"); + this.menu = new instance.web_mobile.Menu(this, "oe_menu", "oe_secondary_menu"); this.menu.start(); }else{ $.mobile.changePage("#oe_menu", "slide", false, true); @@ -119,7 +119,7 @@ openerp.web_mobile.Login = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Header = openerp.web.OldWidget.extend({ +instance.web_mobile.Header = instance.web.OldWidget.extend({ template: "Header", @@ -131,7 +131,7 @@ openerp.web_mobile.Header = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Footer = openerp.web.OldWidget.extend({ +instance.web_mobile.Footer = instance.web.OldWidget.extend({ template: "Footer", @@ -143,7 +143,7 @@ openerp.web_mobile.Footer = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Shortcuts = openerp.web.OldWidget.extend({ +instance.web_mobile.Shortcuts = instance.web.OldWidget.extend({ template: "Shortcuts", @@ -171,7 +171,7 @@ openerp.web_mobile.Shortcuts = openerp.web.OldWidget.extend({ res_id = $shortcut.data('res'); if(!$('[id^="oe_list_'+res_id+'"]').html()){ $('<div id="oe_list_'+res_id+'" data-role="page" data-url="oe_list_'+res_id+'"> </div>').appendTo('#moe'); - this.listview = new openerp.web_mobile.ListView(self, "oe_list_"+res_id, res_id); + this.listview = new instance.web_mobile.ListView(self, "oe_list_"+res_id, res_id); this.listview.start(); }else{ $.mobile.changePage('#oe_list_'+res_id, "slide", false, true); @@ -182,7 +182,7 @@ openerp.web_mobile.Shortcuts = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Menu = openerp.web.OldWidget.extend({ +instance.web_mobile.Menu = instance.web.OldWidget.extend({ template: "Menu", @@ -199,16 +199,16 @@ openerp.web_mobile.Menu = openerp.web.OldWidget.extend({ on_loaded: function(data) { var self = this; this.data = data; - this.header = new openerp.web_mobile.Header(this, "oe_header"); + this.header = new instance.web_mobile.Header(this, "oe_header"); this.header.start(); - this.footer = new openerp.web_mobile.Footer(this, "oe_footer"); + this.footer = new instance.web_mobile.Footer(this, "oe_footer"); this.footer.start(); this.$element.html(this.render(this.data)); this.$element.find("[data-role=header]").find('h1').html('Applications'); this.$element.find("[data-role=header]").find('#home').hide(); this.$element.find("[data-role=footer]").find('#shrotcuts').click(function(){ if(!$('#oe_shortcuts').html().length){ - this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); + this.shortcuts = new instance.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); }else{ $.mobile.changePage($("#oe_shortcuts"), "slide", false, true); @@ -216,7 +216,7 @@ openerp.web_mobile.Menu = openerp.web.OldWidget.extend({ }); this.$element.find("[data-role=footer]").find('#preference').click(function(){ if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); + this.options = new instance.web_mobile.Options(self, "oe_options"); this.options.start(); }else{ $.mobile.changePage("#oe_options", "slide", false, true); @@ -240,7 +240,7 @@ openerp.web_mobile.Menu = openerp.web.OldWidget.extend({ .addClass("secondary_menu"); if(!$('[id^="oe_sec_menu_'+id+'"]').html()){ $('<div id="oe_sec_menu_'+id+'" data-role="page" data-url="oe_sec_menu_'+id+'"> </div>').appendTo('#moe'); - this.secondary = new openerp.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); + this.secondary = new instance.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); this.secondary.start(); }else{ $.mobile.changePage('#oe_sec_menu_'+id, "slide", false, true); @@ -248,7 +248,7 @@ openerp.web_mobile.Menu = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Secondary = openerp.web.OldWidget.extend({ +instance.web_mobile.Secondary = instance.web.OldWidget.extend({ template: "Menu.secondary", @@ -287,7 +287,7 @@ openerp.web_mobile.Secondary = openerp.web.OldWidget.extend({ .addClass("secondary_menu"); if(!$('[id^="oe_sec_menu_'+id+'"]').html()){ $('<div id="oe_sec_menu_'+id+'" data-role="page" data-url="oe_sec_menu_'+id+'"> </div>').appendTo('#moe'); - this.secondary = new openerp.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); + this.secondary = new instance.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); this.secondary.start(); }else{ $.mobile.changePage('#oe_sec_menu_'+id, "slide", false, true); @@ -295,7 +295,7 @@ openerp.web_mobile.Secondary = openerp.web.OldWidget.extend({ }else { if(!$('[id^="oe_list_'+id+'"]').html()){ $('<div id="oe_list_'+id+'" data-role="page" data-url="oe_list_'+id+'"> </div>').appendTo('#moe'); - this.listview = new openerp.web_mobile.ListView(this, "oe_list_"+id, id); + this.listview = new instance.web_mobile.ListView(this, "oe_list_"+id, id); this.listview.start(); }else{ $.mobile.changePage('#oe_list_'+id, "slide", false, true); @@ -305,7 +305,7 @@ openerp.web_mobile.Secondary = openerp.web.OldWidget.extend({ } }); -openerp.web_mobile.Options = openerp.web.OldWidget.extend({ +instance.web_mobile.Options = instance.web.OldWidget.extend({ template: "Options", diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index cf44cca1879..c3c2025ac1f 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -2,9 +2,9 @@ * OpenERP Web Mobile Form View *---------------------------------------------------------*/ -openerp.web_mobile.form_mobile = function (openerp) { +openerp.web_mobile.form_mobile = function (instance) { -openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ +instance.web_mobile.FormView = instance.web.OldWidget.extend({ template: 'FormView', @@ -25,8 +25,8 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ }else{ var view_id = this.viewid; } - this.dataset = new openerp.web.DataSetSearch(this, model, null, null); - var context = new openerp.web.CompoundContext(this.dataset.get_context()); + this.dataset = new instance.web.DataSetSearch(this, model, null, null); + var context = new instance.web.CompoundContext(this.dataset.get_context()); this.dataset.read_slice([]).then(function (result) { for (var i = 0; i < result.length; i++) { if (result[i].id == id) { @@ -71,14 +71,14 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ var head = rel_field.string; if (rel_ids) { var list_ids = []; - var datasearch = new openerp.web.DataSetSearch(self, rel_field.relation, rel_field.context); + var datasearch = new instance.web.DataSetSearch(self, rel_field.relation, rel_field.context); datasearch.domain=[['id', 'in', rel_ids]]; datasearch.read_slice(['name'], {context:rel_field.context, domain: datasearch.domain, limit:80}).then(function(listrec){ _.each(listrec, function(i) { list_ids.push(i.id); }); _.extend(rel_field.context,{"html_name_get" : true}); - var dataset = new openerp.web.DataSet(self, rel_field.relation,rel_field.context); + var dataset = new instance.web.DataSet(self, rel_field.relation,rel_field.context); dataset.name_get(list_ids,function(res){ var additional = ""; if(res['html_name_get']){ @@ -86,7 +86,7 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ } if(!$('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html()){ $('<div id="oe_list_'+relational+'_'+self.element_id+'" data-role="page" data-url="oe_list_'+relational+'_'+self.element_id+'"> </div>').appendTo('#moe'); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(openerp.web.qweb.render("ListView", {'records' : res,'data': additional})); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(instance.web.qweb.render("ListView", {'records' : res,'data': additional})); $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('h1').html(head); $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('#home').click(function(){ $.mobile.changePage("#oe_menu", "slide", false, true); @@ -98,7 +98,7 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ var listid = $(ev.currentTarget).data('id'); if(!$('[id^="oe_form_'+listid+rel_field.relation+'"]').html()){ $('<div id="oe_form_'+listid+rel_field.relation+'" data-role="page" data-url="oe_form_'+listid+rel_field.relation+'"> </div>').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+listid+rel_field.relation, listid, '', head, rel_field.relation, false); + this.formview = new instance.web_mobile.FormView(self, "oe_form_"+listid+rel_field.relation, listid, '', head, rel_field.relation, false); this.formview.start(); }else{ $.mobile.changePage('#oe_form_'+listid+rel_field.relation, "slide", false, true); @@ -126,7 +126,7 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ if(selected_id){ if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ $('<div id="oe_form_'+selected_id+select_model+'" data-role="page" data-url="oe_form_'+selected_id+select_model+'"> </div>').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview = new instance.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); this.formview.start(); }else{ $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); @@ -168,7 +168,7 @@ openerp.web_mobile.FormView = openerp.web.OldWidget.extend({ // Temp: Set as disabled $("#"+getfields[i].attrs.name).attr('disabled', 'true'); if(result.fields[getfields[i].attrs.name]){ - var dateresult = openerp.web.format_value(data[getfields[i].attrs.name], {"widget": result.fields[getfields[i].attrs.name].type}); + var dateresult = instance.web.format_value(data[getfields[i].attrs.name], {"widget": result.fields[getfields[i].attrs.name].type}); $(this).val(dateresult); } } diff --git a/addons/web_mobile/static/src/js/list_mobile.js b/addons/web_mobile/static/src/js/list_mobile.js index 16296df59e7..abe5620a0a3 100644 --- a/addons/web_mobile/static/src/js/list_mobile.js +++ b/addons/web_mobile/static/src/js/list_mobile.js @@ -2,9 +2,9 @@ * OpenERP Web Mobile List View *---------------------------------------------------------*/ -openerp.web_mobile.list_mobile = function (openerp) { +openerp.web_mobile.list_mobile = function (instance) { -openerp.web_mobile.ListView = openerp.web.OldWidget.extend({ +instance.web_mobile.ListView = instance.web.OldWidget.extend({ template: 'ListView', @@ -31,14 +31,14 @@ openerp.web_mobile.ListView = openerp.web.OldWidget.extend({ on_search_data: function(ev){ var self = this; var list_ids = []; - var datasearch = new openerp.web.DataSetSearch(self, self.action.res_model,self.action.context); + var datasearch = new instance.web.DataSetSearch(self, self.action.res_model,self.action.context); datasearch.domain = self.action.domain; datasearch.read_slice(['name'], {context:datasearch.context, domain: datasearch.domain, limit:80}).then(function(listresult){ _.each(listresult, function(i) { list_ids.push(i.id); }); _.extend(self.action.context,{"html_name_get" : true}); - var dataset = new openerp.web.DataSet(self, datasearch.model,datasearch.context); + var dataset = new instance.web.DataSet(self, datasearch.model,datasearch.context); dataset.name_get(list_ids,function(res){ var additional = ""; if(res['html_name_get']){ @@ -64,7 +64,7 @@ openerp.web_mobile.ListView = openerp.web.OldWidget.extend({ head_title = $.trim($record.text()); if(!$('[id^="oe_form_'+id+this.action.res_model+'"]').html()){ $('<div id="oe_form_'+id+this.action.res_model+'" data-role="page" data-url="oe_form_'+id+this.action.res_model+'"> </div>').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(this, "oe_form_"+id+this.action.res_model, id, this.action, head_title, '' ,''); + this.formview = new instance.web_mobile.FormView(this, "oe_form_"+id+this.action.res_model, id, this.action, head_title, '' ,''); this.formview.start(); }else{ $.mobile.changePage('#oe_form_'+id+this.action.res_model, "slide", false, true); diff --git a/addons/web_process/i18n/bn.po b/addons/web_process/i18n/bn.po new file mode 100644 index 00000000000..177a0babae9 --- /dev/null +++ b/addons/web_process/i18n/bn.po @@ -0,0 +1,118 @@ +# Bengali translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 19:19+0100\n" +"PO-Revision-Date: 2012-04-12 22:04+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bengali <bn@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-04-13 05:38+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:261 +msgid "Cancel" +msgstr "বাতিল করুন" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:262 +msgid "Save" +msgstr "সংরক্ষণ করুন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:6 +msgid "Process View" +msgstr "প্রক্রিয়াদর্শন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Documentation" +msgstr "নথিকরন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Read Documentation Online" +msgstr "অনলাইনে নথিপত্র পরুন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Forum" +msgstr "মুক্ত আলোচনাস্থল" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Community Discussion" +msgstr "সাম্প্রদায়িক আলোচনা" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Books" +msgstr "বইগুলি" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Get the books" +msgstr "বইগুলি নাও" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "OpenERP Enterprise" +msgstr "ওপেন-ই-আর-পি শিল্পোদ্যোগ" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "Purchase OpenERP Enterprise" +msgstr "ওপেন-ই-আর-পি শিল্পোদ্যোগ কিনুন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:52 +msgid "Process" +msgstr "প্রক্রিয়া" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:56 +msgid "Notes:" +msgstr "টীকাগুলি" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "Last modified by:" +msgstr "সর্বশেষ পরিবর্তন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "N/A" +msgstr "প্রযোজ্য নয়" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:62 +msgid "Subflows:" +msgstr "উপপ্রবাহগুলি:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:75 +msgid "Related:" +msgstr "সম্পর্কিত:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:88 +msgid "Select Process" +msgstr "প্রক্রিয়া নির্বাচন করুন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:98 +msgid "Select" +msgstr "নির্বাচন করুন" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:109 +msgid "Edit Process" +msgstr "প্রক্রিয়া সম্পাদনা করুন" diff --git a/addons/web_process/i18n/bs.po b/addons/web_process/i18n/bs.po new file mode 100644 index 00000000000..7685aeea7ff --- /dev/null +++ b/addons/web_process/i18n/bs.po @@ -0,0 +1,118 @@ +# Bosnian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 19:19+0100\n" +"PO-Revision-Date: 2012-04-17 17:00+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Bosnian <bs@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:261 +msgid "Cancel" +msgstr "Otkaži" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:262 +msgid "Save" +msgstr "Spasi" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:6 +msgid "Process View" +msgstr "Pregled Procesa" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Documentation" +msgstr "Dokumentacija" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Read Documentation Online" +msgstr "Čitaj Dokumentaciju Online" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Forum" +msgstr "Forum" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Community Discussion" +msgstr "Zajednička Diskusija" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Books" +msgstr "Knjige" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Get the books" +msgstr "Nabavite knjige" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "OpenERP Enterprise" +msgstr "Pokreni OpenERP" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "Purchase OpenERP Enterprise" +msgstr "Kupi OpenERP Enterprise" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:52 +msgid "Process" +msgstr "Proces" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:56 +msgid "Notes:" +msgstr "Bilješke:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "Last modified by:" +msgstr "Posljednje modifikovano od:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "N/A" +msgstr "N/A" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:62 +msgid "Subflows:" +msgstr "" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:75 +msgid "Related:" +msgstr "Srodnost:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:88 +msgid "Select Process" +msgstr "Odabrani Proces" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:98 +msgid "Select" +msgstr "Izaberi" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:109 +msgid "Edit Process" +msgstr "Edituj proces" diff --git a/addons/web_process/i18n/es_CL.po b/addons/web_process/i18n/es_CL.po new file mode 100644 index 00000000000..e5a46f09bcd --- /dev/null +++ b/addons/web_process/i18n/es_CL.po @@ -0,0 +1,118 @@ +# Spanish (Chile) translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 19:19+0100\n" +"PO-Revision-Date: 2012-04-14 15:22+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Chile) <es_CL@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-04-15 04:48+0000\n" +"X-Generator: Launchpad (build 15070)\n" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:261 +msgid "Cancel" +msgstr "Cancelar" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:262 +msgid "Save" +msgstr "Guardar" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:6 +msgid "Process View" +msgstr "Vista del proceso" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Documentation" +msgstr "Documentación" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Read Documentation Online" +msgstr "Leer documentación online" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Forum" +msgstr "Foro" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Community Discussion" +msgstr "Debate de la comunidad" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Books" +msgstr "Libros" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Get the books" +msgstr "Obtener los libros" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "OpenERP Enterprise" +msgstr "OpenERP Enterprise" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "Purchase OpenERP Enterprise" +msgstr "Comprar OpenERP Enterprise" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:52 +msgid "Process" +msgstr "Proceso" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:56 +msgid "Notes:" +msgstr "Observaciones:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "Last modified by:" +msgstr "Última modificación por:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "N/A" +msgstr "N/D" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:62 +msgid "Subflows:" +msgstr "Subflujos:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:75 +msgid "Related:" +msgstr "Relacionado:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:88 +msgid "Select Process" +msgstr "Seleccionar proceso" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:98 +msgid "Select" +msgstr "Seleccionar" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:109 +msgid "Edit Process" +msgstr "Editar Proceso" diff --git a/addons/web_process/i18n/sv.po b/addons/web_process/i18n/sv.po new file mode 100644 index 00000000000..f054ab1ce45 --- /dev/null +++ b/addons/web_process/i18n/sv.po @@ -0,0 +1,118 @@ +# Swedish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-07 19:19+0100\n" +"PO-Revision-Date: 2012-04-17 12:42+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Swedish <sv@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-04-18 04:57+0000\n" +"X-Generator: Launchpad (build 15108)\n" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:261 +msgid "Cancel" +msgstr "Avbryt" + +#. openerp-web +#: addons/web_process/static/src/js/process.js:262 +msgid "Save" +msgstr "Spara" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:6 +msgid "Process View" +msgstr "Processvy" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Documentation" +msgstr "Dokumentation" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:19 +msgid "Read Documentation Online" +msgstr "Läs online-dokumentationen" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Forum" +msgstr "Forum" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:25 +msgid "Community Discussion" +msgstr "Diskussion i användarföreningen" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Books" +msgstr "Böcker" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:31 +msgid "Get the books" +msgstr "Komma över böckerna" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "OpenERP Enterprise" +msgstr "OpenERP Enterprise" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:37 +msgid "Purchase OpenERP Enterprise" +msgstr "Köp OpenERP Enterprise" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:52 +msgid "Process" +msgstr "Process" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:56 +msgid "Notes:" +msgstr "Anteckningar:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "Last modified by:" +msgstr "Senast ändrad av:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:59 +msgid "N/A" +msgstr "Ej tillgänglig" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:62 +msgid "Subflows:" +msgstr "Underflöde" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:75 +msgid "Related:" +msgstr "Relaterad:" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:88 +msgid "Select Process" +msgstr "Välj process" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:98 +msgid "Select" +msgstr "Välj" + +#. openerp-web +#: addons/web_process/static/src/xml/web_process.xml:109 +msgid "Edit Process" +msgstr "Ändra processen" diff --git a/addons/web_process/static/src/js/process.js b/addons/web_process/static/src/js/process.js index 5a4fbfd6aaf..272513919b4 100644 --- a/addons/web_process/static/src/js/process.js +++ b/addons/web_process/static/src/js/process.js @@ -1,7 +1,7 @@ -openerp.web_process = function (openerp) { - var QWeb = openerp.web.qweb, - _t = openerp.web._t; - openerp.web.ViewManager.include({ +openerp.web_process = function (instance) { + var QWeb = instance.web.qweb, + _t = instance.web._t; + instance.web.ViewManager.include({ start: function() { this._super(); this.process_check(); @@ -15,7 +15,7 @@ openerp.web_process = function (openerp) { grandparent = this.getParent() && this.getParent().getParent(), view = this.views[this.views_src[0].view_type], $process_view = this.$element.find('.oe-process-view'); - if (!(grandparent instanceof openerp.web.WebClient) || + if (!(grandparent instanceof instance.web.WebClient) || !(view.view_type === this.views_src[0].view_type && view.view_id === this.views_src[0].view_id)) { $process_view.hide(); @@ -28,7 +28,7 @@ openerp.web_process = function (openerp) { initialize_process_view: function() { var self = this; this.record_id = false; - if(this.active_view == 'page' || this.active_view == 'form') { + if(this.active_view == 'form') { this.record_id = this.views[this.active_view].controller.datarecord.id; } @@ -86,7 +86,7 @@ openerp.web_process = function (openerp) { if(!this.subflow_model) { def.resolve(this.action ? (this.action.help!=false ? this.action.help : 'Help: Not Defined') : 'Help: Not Defined'); } else { - var dataset = new openerp.web.DataSetSearch(this, "ir.actions.act_window", this.session.context, []); + var dataset = new instance.web.DataSetSearch(this, "ir.actions.act_window", this.session.context, []); dataset .read_slice(['help'], { @@ -107,7 +107,7 @@ openerp.web_process = function (openerp) { if(this.process_id) return def.resolve().promise(); - this.process_dataset = new openerp.web.DataSet(this, "process.process", this.session.context); + this.process_dataset = new instance.web.DataSet(this, "process.process", this.session.context); this.process_dataset .call("search_by_model", [self.process_model,self.session.context]) .done(function(res) { @@ -237,7 +237,7 @@ openerp.web_process = function (openerp) { }, jump_to_view: function(model, id) { var self = this; - var dataset = new openerp.web.DataSet(this, 'ir.values', this.session.context); + var dataset = new instance.web.DataSet(this, 'ir.values', this.session.context); dataset.call('get', ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context], function(res) { @@ -246,7 +246,7 @@ openerp.web_process = function (openerp) { action_id: action.id, context: dataset.context }, function(result) { - var action_manager = new openerp.web.ActionManager(self); + var action_manager = new instance.web.ActionManager(self); action_manager.replace(self.$element); action_manager.do_action(result.result); }); @@ -254,8 +254,8 @@ openerp.web_process = function (openerp) { }, edit_process_view: function() { var self = this; - var action_manager = new openerp.web.ActionManager(this); - var dialog = new openerp.web.Dialog(this, { + var action_manager = new instance.web.ActionManager(this); + var dialog = new instance.web.Dialog(this, { width: 800, buttons : [ {text: _t("Cancel"), click: function() { $(this).dialog('destroy'); }}, diff --git a/addons/web_tests/static/src/js/web_tests.js b/addons/web_tests/static/src/js/web_tests.js index f98dd95f66f..e6e6079ede5 100644 --- a/addons/web_tests/static/src/js/web_tests.js +++ b/addons/web_tests/static/src/js/web_tests.js @@ -1,16 +1,16 @@ -openerp.web_tests = function (db) { - db.web.client_actions.add( +openerp.web_tests = function (instance) { + instance.web.client_actions.add( 'buncha-forms', 'instance.web_tests.BunchaForms'); - db.web_tests = {}; - db.web_tests.BunchaForms = db.web.OldWidget.extend({ + instance.web_tests = {}; + instance.web_tests.BunchaForms = instance.web.OldWidget.extend({ init: function (parent) { this._super(parent); - this.dataset = new db.web.DataSetSearch(this, 'test.listview.relations'); - this.form = new db.web.FormView(this, this.dataset, false, { + this.dataset = new instance.web.DataSetSearch(this, 'test.listview.relations'); + this.form = new instance.web.FormView(this, this.dataset, false, { action_buttons: false, pager: false }); - this.form.registry = db.web.form.readonly; + this.form.registry = instance.web.form.readonly; }, render: function () { return '<div class="oe-bunchaforms"></div>'; |
