--- a/.hgtags Mon May 16 16:24:00 2011 +0200
+++ b/.hgtags Wed Jul 20 18:21:47 2011 +0200
@@ -188,5 +188,25 @@
77318f1ec4aae3523d455e884daf3708c3c79af7 cubicweb-debian-version-3.11.1-1
56ae3cd5f8553678a2b1d4121b61241598d0ca68 cubicweb-version-3.11.2
954b5b51cd9278eb45d66be1967064d01ab08453 cubicweb-debian-version-3.11.2-1
+fd502219eb76f4bfd239d838a498a1d1e8204baf cubicweb-version-3.12.0
+92b56939b7c77bbf443b893c495a20f19bc30702 cubicweb-debian-version-3.12.0-1
+59701627adba73ee97529f6ea0e250a0f3748e32 cubicweb-version-3.12.1
+07e2c9c7df2617c5ecfa84cb819b3ee8ef91d1f2 cubicweb-debian-version-3.12.1-1
+5a9b6bc5653807500c30a7eb0e95b90fd714fec3 cubicweb-version-3.12.2
+6d418fb3ffed273562aae411efe323d5138b592a cubicweb-debian-version-3.12.2-1
b7a124f9aed2c7c9c86c6349ddd9f0a07023f0ca cubicweb-version-3.11.3
b3c6702761a18a41fdbb7bc1083f92aefce07765 cubicweb-debian-version-3.11.3-1
+e712bc6f1f71684f032bfcb9bb151a066c707dec cubicweb-version-3.12.3
+ba8fe4f2e408c3fdf6c297cd42c2577dcac50e71 cubicweb-debian-version-3.12.3-1
+5cd0dbc26882f60e3f11ec55e7f058d94505e7ed cubicweb-version-3.12.4
+7c4d34a5ec57f927a70cbc7af7fa8310c847ac42 cubicweb-debian-version-3.12.4-1
+ae33197db1f08d42c9df49563f7b15246f8c8e58 cubicweb-version-3.12.5
+6dfe78a0797ccc34962510f8c2a57f63d65ce41e cubicweb-debian-version-3.12.5-1
+a18dac758150fe9c1f9e4958d898717c32a8f679 cubicweb-version-3.12.6
+105767487c7075dbcce36474f1af0485985cbf2c cubicweb-debian-version-3.12.6-1
+628fe57ce746c1dac87fb1b078b2026057df894e cubicweb-version-3.12.7
+a07517985136bbbfa6610c428a1b42cd04cd530b cubicweb-debian-version-3.12.7-1
+50122a47ce4fb2ecbf3cf20ed2777f4276c93609 cubicweb-version-3.12.8
+cf49ed55685a810d8d73585330ad1a57cc76260d cubicweb-debian-version-3.12.8-1
+cb2990aaa63cbfe593bcf3afdbb9071e4c76815a cubicweb-version-3.12.9
+92464e39134c70e4ddbe6cd78a6e3338a3b88b05 cubicweb-debian-version-3.12.9-1
--- a/README Mon May 16 16:24:00 2011 +0200
+++ b/README Wed Jul 20 18:21:47 2011 +0200
@@ -25,12 +25,12 @@
cubicweb-ctl start -D myblog
sensible-browser http://localhost:8080/
-Details at http://www.cubicweb.org/doc/en/intro/tutorial/blog-in-five-minutes
+Details at http://docs.cubicweb.org/tutorials/base/blog-in-five-minutes
Documentation
-------------
-Look in the doc/ subdirectory or read http://www.cubicweb.org/doc/en/
+Look in the doc/ subdirectory or read http://docs.cubicweb.org/
--- a/__pkginfo__.py Mon May 16 16:24:00 2011 +0200
+++ b/__pkginfo__.py Wed Jul 20 18:21:47 2011 +0200
@@ -22,7 +22,7 @@
modname = distname = "cubicweb"
-numversion = (3, 11, 3)
+numversion = (3, 12, 9)
version = '.'.join(str(num) for num in numversion)
description = "a repository of entities / relations for knowledge management"
@@ -43,7 +43,7 @@
'logilab-common': '>= 0.55.2',
'logilab-mtconverter': '>= 0.8.0',
'rql': '>= 0.28.0',
- 'yams': '>= 0.30.4',
+ 'yams': '>= 0.32.0',
'docutils': '>= 0.6',
#gettext # for xgettext, msgcat, etc...
# web dependancies
@@ -52,7 +52,7 @@
'Twisted': '',
# XXX graphviz
# server dependencies
- 'logilab-database': '>= 1.4.0',
+ 'logilab-database': '>= 1.5.0',
'pysqlite': '>= 2.5.5', # XXX install pysqlite2
}
--- a/cwconfig.py Mon May 16 16:24:00 2011 +0200
+++ b/cwconfig.py Wed Jul 20 18:21:47 2011 +0200
@@ -1070,9 +1070,10 @@
def instance_md5_version(self):
import hashlib
infos = []
- for pkg in self.cubes():
+ for pkg in sorted(self.cubes()):
version = self.cube_version(pkg)
infos.append('%s-%s' % (pkg, version))
+ infos.append('cubicweb-%s' % str(self.cubicweb_version()))
return hashlib.md5(';'.join(infos)).hexdigest()
def load_configuration(self):
--- a/cwctl.py Mon May 16 16:24:00 2011 +0200
+++ b/cwctl.py Wed Jul 20 18:21:47 2011 +0200
@@ -152,16 +152,17 @@
print '*'*72
if not ASK.confirm('%s instance %r ?' % (self.name, appid)):
continue
- status = max(status, self.run_arg(appid))
+ try:
+ status = max(status, self.run_arg(appid))
+ except (KeyboardInterrupt, SystemExit):
+ print >> sys.stderr, '%s aborted' % self.name
+ return 2 # specific error code
sys.exit(status)
def run_arg(self, appid):
cmdmeth = getattr(self, '%s_instance' % self.name)
try:
status = cmdmeth(appid)
- except (KeyboardInterrupt, SystemExit):
- print >> sys.stderr, '%s aborted' % self.name
- return 2 # specific error code
except (ExecutionError, ConfigurationError), ex:
print >> sys.stderr, 'instance %s not %s: %s' % (
appid, self.actionverb, ex)
@@ -757,18 +758,16 @@
applcubicwebversion = vcconf.get('cubicweb')
if cubicwebversion > applcubicwebversion:
toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion))
- if not self.config.fs_only and not toupgrade:
- print '-> no data migration needed for instance %s.' % appid
- self.i18nupgrade(config)
- mih.shutdown()
- return
- for cube, fromversion, toversion in toupgrade:
- print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube)
# only stop once we're sure we have something to do
if not (CWDEV or self.config.nostartstop):
StopInstanceCommand(self.logger).stop_instance(appid)
# run cubicweb/componants migration scripts
- mih.migrate(vcconf, reversed(toupgrade), self.config)
+ if self.config.fs_only or toupgrade:
+ for cube, fromversion, toversion in toupgrade:
+ print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube)
+ mih.migrate(vcconf, reversed(toupgrade), self.config)
+ else:
+ print '-> no data migration needed for instance %s.' % appid
# rewrite main configuration file
mih.rewrite_configuration()
mih.shutdown()
@@ -905,6 +904,7 @@
scripts, args = self.cmdline_parser.largs[1:], self.cmdline_parser.rargs
for script in scripts:
mih.cmd_process_script(script, scriptargs=args)
+ mih.commit()
else:
mih.interactive_shell()
finally:
--- a/cwvreg.py Mon May 16 16:24:00 2011 +0200
+++ b/cwvreg.py Wed Jul 20 18:21:47 2011 +0200
@@ -312,6 +312,10 @@
kwargs['clear'] = True
super(ETypeRegistry, self).register(obj, **kwargs)
+ def iter_classes(self):
+ for etype in self.vreg.schema.entities():
+ yield self.etype_class(etype)
+
@cached
def parent_classes(self, etype):
if etype == 'Any':
@@ -369,6 +373,16 @@
cls.__initialize__(self.schema)
return cls
+ def fetch_attrs(self, targettypes):
+ """return intersection of fetch_attrs of each entity type in
+ `targettypes`
+ """
+ fetchattrs_list = []
+ for ttype in targettypes:
+ etypecls = self.etype_class(ttype)
+ fetchattrs_list.append(set(etypecls.fetch_attrs))
+ return reduce(set.intersection, fetchattrs_list)
+
VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
@@ -742,7 +756,7 @@
sitewide=False):
"""register a given property"""
properties = self['propertydefs']
- assert type in YAMS_TO_PY
+ assert type in YAMS_TO_PY, 'unknown type %s' % type
properties[key] = {'type': type, 'vocabulary': vocabulary,
'default': default, 'help': help,
'sitewide': sitewide}
@@ -835,18 +849,24 @@
return self['views'].select(__vid, req, rset=rset, **kwargs)
+import decimal
from datetime import datetime, date, time, timedelta
-YAMS_TO_PY = {
- 'Boolean': bool,
+YAMS_TO_PY = { # XXX unify with yams.constraints.BASE_CONVERTERS?
'String' : unicode,
+ 'Bytes': Binary,
'Password': str,
- 'Bytes': Binary,
+
+ 'Boolean': bool,
'Int': int,
'Float': float,
- 'Date': date,
- 'Datetime': datetime,
- 'Time': time,
- 'Interval': timedelta,
+ 'Decimal': decimal.Decimal,
+
+ 'Date': date,
+ 'Datetime': datetime,
+ 'TZDatetime': datetime,
+ 'Time': time,
+ 'TZTime': time,
+ 'Interval': timedelta,
}
--- a/dataimport.py Mon May 16 16:24:00 2011 +0200
+++ b/dataimport.py Wed Jul 20 18:21:47 2011 +0200
@@ -133,7 +133,7 @@
"""
for idx, item in enumerate(iterable):
yield item
- if idx % number:
+ if not idx % number:
func()
func()
--- a/dbapi.py Mon May 16 16:24:00 2011 +0200
+++ b/dbapi.py Wed Jul 20 18:21:47 2011 +0200
@@ -30,6 +30,7 @@
from itertools import count
from warnings import warn
from os.path import join
+from uuid import uuid4
from logilab.common.logging_ext import set_log_methods
from logilab.common.decorators import monkeypatch
@@ -240,13 +241,14 @@
self.cnx = cnx
self.data = {}
self.login = login
+ self.mtime = time()
# dbapi session identifier is the same as the first connection
# identifier, but may later differ in case of auto-reconnection as done
# by the web authentication manager (in cw.web.views.authentication)
if cnx is not None:
self.sessionid = cnx.sessionid
else:
- self.sessionid = None
+ self.sessionid = uuid4().hex
@property
def anonymous_session(self):
--- a/debian/changelog Mon May 16 16:24:00 2011 +0200
+++ b/debian/changelog Wed Jul 20 18:21:47 2011 +0200
@@ -1,3 +1,63 @@
+cubicweb (3.12.9-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Tue, 12 Jul 2011 11:30:10 +0200
+
+cubicweb (3.12.8-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 27 Jun 2011 13:58:58 +0200
+
+cubicweb (3.12.7-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Tue, 21 Jun 2011 14:47:14 +0200
+
+cubicweb (3.12.6-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 09 Jun 2011 16:09:50 +0200
+
+cubicweb (3.12.5-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 23 May 2011 10:27:42 +0200
+
+cubicweb (3.12.4-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 11 May 2011 12:28:42 +0200
+
+cubicweb (3.12.3-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 05 May 2011 16:21:33 +0200
+
+cubicweb (3.12.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Nicolas Chauvat <nicolas.chauvat@logilab.fr> Mon, 11 Apr 2011 22:15:21 +0200
+
+cubicweb (3.12.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 06 Apr 2011 23:25:12 +0200
+
+cubicweb (3.12.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Alexandre Fayolle <alexandre.fayolle@logilab.fr> Fri, 01 Apr 2011 15:59:37 +0200
+
cubicweb (3.11.3-1) unstable; urgency=low
* new upstream release
@@ -6,7 +66,7 @@
cubicweb (3.11.2-1) unstable; urgency=low
- * new upstream release
+ * new upstream release
-- Nicolas Chauvat <nicolas.chauvat@logilab.fr> Mon, 28 Mar 2011 19:18:54 +0200
--- a/debian/control Mon May 16 16:24:00 2011 +0200
+++ b/debian/control Wed Jul 20 18:21:47 2011 +0200
@@ -8,6 +8,8 @@
Aurélien Campéas <aurelien.campeas@logilab.fr>,
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Build-Depends: debhelper (>= 7), python (>= 2.5), python-central (>= 0.5)
+# for the documentation:
+# python-sphinx, python-logilab-common, python-unittest2,
Standards-Version: 3.9.1
Homepage: http://www.cubicweb.org
XS-Python-Version: >= 2.5, << 2.7
@@ -33,7 +35,7 @@
Conflicts: cubicweb-multisources
Replaces: cubicweb-multisources
Provides: cubicweb-multisources
-Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.4.0), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2
+Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.5.0), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2
Recommends: pyro (<< 4.0.0), cubicweb-documentation (= ${source:Version})
Description: server part of the CubicWeb framework
CubicWeb is a semantic web application framework.
@@ -97,7 +99,7 @@
Package: cubicweb-common
Architecture: all
XB-Python-Version: ${python:Versions}
-Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.2), python-yams (>= 0.30.4), python-rql (>= 0.28.0), python-lxml
+Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.2), python-yams (>= 0.32.0), python-rql (>= 0.28.0), python-lxml
Recommends: python-simpletal (>= 4.0), python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
@@ -133,6 +135,7 @@
Package: cubicweb-documentation
+Architecture: all
Recommends: doc-base
Description: documentation for the CubicWeb framework
CubicWeb is a semantic web application framework.
--- a/debian/rules Mon May 16 16:24:00 2011 +0200
+++ b/debian/rules Wed Jul 20 18:21:47 2011 +0200
@@ -15,6 +15,9 @@
# cd doc && make
# FIXME cleanup and use sphinx-build as build-depends ?
NO_SETUPTOOLS=1 python setup.py build
+ # XXX uncomment this and associated build-depends in control
+ #when necessary sphinx version is in all built distribution
+ #PYTHONPATH=$(CURDIR)/.. $(MAKE) -C doc/book/en all
touch build-stamp
clean:
--- a/devtools/__init__.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/__init__.py Wed Jul 20 18:21:47 2011 +0200
@@ -384,7 +384,7 @@
def get_cnx(self):
- """return Connection object ont he current repository"""
+ """return Connection object on the current repository"""
from cubicweb.dbapi import in_memory_cnx
repo = self.get_repo()
sources = self.config.sources()
@@ -527,11 +527,19 @@
return get_db_helper('postgres')
@property
- @cached
def dbcnx(self):
- from cubicweb.server.serverctl import _db_sys_cnx
- return _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER',
- interactive=False)
+ try:
+ return self._cnx
+ except AttributeError:
+ from cubicweb.server.serverctl import _db_sys_cnx
+ try:
+ self._cnx = _db_sys_cnx(
+ self.system_source, 'CREATE DATABASE and / or USER',
+ interactive=False)
+ return self._cnx
+ except Exception:
+ self._cnx = None
+ raise
@property
@cached
@@ -569,17 +577,19 @@
cnx.close()
init_repository(self.config, interactive=False)
except:
- self.dbcnx.rollback()
+ if self.dbcnx is not None:
+ self.dbcnx.rollback()
print >> sys.stderr, 'building', self.dbname, 'failed'
#self._drop(self.dbname)
raise
def helper_clear_cache(self):
- self.dbcnx.commit()
- self.dbcnx.close()
- clear_cache(self, 'dbcnx')
+ if self.dbcnx is not None:
+ self.dbcnx.commit()
+ self.dbcnx.close()
+ del self._cnx
+ clear_cache(self, 'cursor')
clear_cache(self, 'helper')
- clear_cache(self, 'cursor')
def __del__(self):
self.helper_clear_cache()
@@ -600,15 +610,18 @@
def _backup_database(self, db_id):
"""Actual backup the current database.
- return a value to be stored in db_cache to allow restoration"""
+ return a value to be stored in db_cache to allow restoration
+ """
from cubicweb.server.serverctl import createdb
orig_name = self.system_source['db-name']
try:
backup_name = self._backup_name(db_id)
self._drop(backup_name)
self.system_source['db-name'] = backup_name
+ self._repo.turn_repo_off()
createdb(self.helper, self.system_source, self.dbcnx, self.cursor, template=orig_name)
self.dbcnx.commit()
+ self._repo.turn_repo_on()
return backup_name
finally:
self.system_source['db-name'] = orig_name
--- a/devtools/fake.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/fake.py Wed Jul 20 18:21:47 2011 +0200
@@ -58,7 +58,7 @@
if not (args or 'vreg' in kwargs):
kwargs['vreg'] = CubicWebVRegistry(FakeConfig(), initlog=False)
kwargs['https'] = False
- self._url = kwargs.pop('url', 'view?rql=Blop&vid=blop')
+ self._url = kwargs.pop('url', None) or 'view?rql=Blop&vid=blop'
super(FakeRequest, self).__init__(*args, **kwargs)
self._session_data = {}
self._headers_in = Headers()
--- a/devtools/fill.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/fill.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: iso-8859-1 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -152,6 +152,8 @@
base = datetime(randint(2000, 2004), randint(1, 12), randint(1, 28), 11, index%60)
return self._constrained_generate(entity, attrname, base, timedelta(hours=1), index)
+ generate_tzdatetime = generate_datetime # XXX implementation should add a timezone
+
def generate_date(self, entity, attrname, index):
"""generates a random date (format is 'yyyy-mm-dd')"""
base = date(randint(2000, 2010), 1, 1) + timedelta(randint(1, 365))
@@ -166,6 +168,8 @@
"""generates a random time (format is ' HH:MM')"""
return time(11, index%60) #'11:%02d' % (index % 60)
+ generate_tztime = generate_time # XXX implementation should add a timezone
+
def generate_bytes(self, entity, attrname, index, format=None):
fakefile = Binary("%s%s" % (attrname, index))
fakefile.filename = u"file_%s" % attrname
@@ -441,7 +445,7 @@
constraints = [c for c in rdef.constraints
if isinstance(c, RQLConstraint)]
if constraints:
- restrictions = ', '.join(c.restriction for c in constraints)
+ restrictions = ', '.join(c.expression for c in constraints)
q += ', %s' % restrictions
# restrict object eids if possible
# XXX the attempt to restrict below in completely wrong
--- a/devtools/httptest.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/httptest.py Wed Jul 20 18:21:47 2011 +0200
@@ -145,7 +145,7 @@
if passwd is None:
passwd = user
self.login(user)
- response = self.web_get("?__login=%s&__password=%s" %
+ response = self.web_get("login?__login=%s&__password=%s" %
(user, passwd))
assert response.status == httplib.SEE_OTHER, response.status
self._ident_cookie = response.getheader('Set-Cookie')
--- a/devtools/qunit.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/qunit.py Wed Jul 20 18:21:47 2011 +0200
@@ -5,7 +5,7 @@
from Queue import Queue, Empty
from subprocess import Popen, check_call, CalledProcessError
from shutil import rmtree, copy as copyfile
-from uuid import uuid4
+from uuid import uuid4
# imported by default to simplify further import statements
from logilab.common.testlib import unittest_main, with_tempdir, InnerTest, Tags
--- a/devtools/repotest.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/repotest.py Wed Jul 20 18:21:47 2011 +0200
@@ -371,8 +371,13 @@
_orig_select_principal = rqlannotation._select_principal
def _select_principal(scope, relations):
+ def sort_key(something):
+ try:
+ return something.r_type
+ except AttributeError:
+ return (something[0].r_type, something[1])
return _orig_select_principal(scope, relations,
- _sort=lambda rels: sorted(rels, key=lambda x: x.r_type))
+ _sort=lambda rels: sorted(rels, key=sort_key))
try:
from cubicweb.server.msplanner import PartPlanInformation
--- a/devtools/testlib.py Mon May 16 16:24:00 2011 +0200
+++ b/devtools/testlib.py Wed Jul 20 18:21:47 2011 +0200
@@ -30,6 +30,7 @@
from math import log
from contextlib import contextmanager
from warnings import warn
+from types import NoneType
import yams.schema
@@ -37,16 +38,15 @@
from logilab.common.pytest import nocoverage, pause_tracing, resume_tracing
from logilab.common.debugger import Debugger
from logilab.common.umessage import message_from_string
-from logilab.common.decorators import cached, classproperty, clear_cache
+from logilab.common.decorators import cached, classproperty, clear_cache, iclassmethod
from logilab.common.deprecation import deprecated, class_deprecated
from logilab.common.shellutils import getlogin
from cubicweb import ValidationError, NoSelectableObject, AuthenticationError
-from cubicweb import cwconfig, devtools, web, server
-from cubicweb.dbapi import ProgrammingError, DBAPISession, repo_connect
+from cubicweb import cwconfig, dbapi, devtools, web, server
from cubicweb.sobjects import notification
from cubicweb.web import Redirect, application
-from cubicweb.server.session import security_enabled
+from cubicweb.server.session import Session, security_enabled
from cubicweb.server.hook import SendMailOp
from cubicweb.devtools import SYSTEM_ENTITIES, SYSTEM_RELATIONS, VIEW_VALIDATORS
from cubicweb.devtools import BASE_URL, fake, htmlparser, DEFAULT_EMPTY_DB_ID
@@ -252,7 +252,7 @@
# cnx is now an instance property that use a class protected attributes.
cls.set_cnx(cnx)
cls.vreg = cls.repo.vreg
- cls.websession = DBAPISession(cnx, cls.admlogin)
+ cls.websession = dbapi.DBAPISession(cnx, cls.admlogin)
cls._orig_cnx = (cnx, cls.websession)
cls.config.repository = lambda x=None: cls.repo
@@ -354,23 +354,64 @@
else:
return req.user
- def create_user(self, login, groups=('users',), password=None, req=None,
- commit=True, **kwargs):
+ @iclassmethod # XXX turn into a class method
+ def create_user(self, req, login=None, groups=('users',), password=None,
+ email=None, commit=True, **kwargs):
"""create and return a new user entity"""
+ if isinstance(req, basestring):
+ warn('[3.12] create_user arguments are now (req, login[, groups, password, commit, **kwargs])',
+ DeprecationWarning, stacklevel=2)
+ if not isinstance(groups, (tuple, list)):
+ password = groups
+ groups = login
+ elif isinstance(login, tuple):
+ groups = login
+ login = req
+ assert not isinstance(self, type)
+ req = self._orig_cnx[0].request()
if password is None:
password = login.encode('utf8')
- if req is None:
- req = self._orig_cnx[0].request()
user = req.create_entity('CWUser', login=unicode(login),
upassword=password, **kwargs)
req.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
% ','.join(repr(str(g)) for g in groups),
{'x': user.eid})
+ if email is not None:
+ req.create_entity('EmailAddress', address=unicode(email),
+ reverse_primary_email=user)
user.cw_clear_relation_cache('in_group', 'subject')
if commit:
- req.cnx.commit()
+ try:
+ req.commit() # req is a session
+ except AttributeError:
+ req.cnx.commit()
return user
+ @iclassmethod # XXX turn into a class method
+ def grant_permission(self, session, entity, group, pname=None, plabel=None):
+ """insert a permission on an entity. Will have to commit the main
+ connection to be considered
+ """
+ if not isinstance(session, Session):
+ warn('[3.12] grant_permission arguments are now (session, entity, group, pname[, plabel])',
+ DeprecationWarning, stacklevel=2)
+ plabel = pname
+ pname = group
+ group = entity
+ entity = session
+ assert not isinstance(self, type)
+ session = self.session
+ pname = unicode(pname)
+ plabel = plabel and unicode(plabel) or unicode(group)
+ e = getattr(entity, 'eid', entity)
+ with security_enabled(session, False, False):
+ peid = session.execute(
+ 'INSERT CWPermission X: X name %(pname)s, X label %(plabel)s,'
+ 'X require_group G, E require_permission X '
+ 'WHERE G name %(group)s, E eid %(e)s',
+ locals())[0][0]
+ return peid
+
def login(self, login, **kwargs):
"""return a connection for the given login/password"""
if login == self.admlogin:
@@ -380,8 +421,8 @@
autoclose = kwargs.pop('autoclose', True)
if not kwargs:
kwargs['password'] = str(login)
- self.set_cnx(repo_connect(self.repo, unicode(login), **kwargs))
- self.websession = DBAPISession(self.cnx)
+ self.set_cnx(dbapi.repo_connect(self.repo, unicode(login), **kwargs))
+ self.websession = dbapi.DBAPISession(self.cnx)
if login == self.vreg.config.anonymous_user()[0]:
self.cnx.anonymous_connection = True
if autoclose:
@@ -423,7 +464,7 @@
def rollback(self):
try:
self.cnx.rollback()
- except ProgrammingError:
+ except dbapi.ProgrammingError:
pass # connection closed
finally:
self.session.set_pool() # ensure pool still set after commit
@@ -439,21 +480,6 @@
# other utilities #########################################################
- def grant_permission(self, entity, group, pname, plabel=None):
- """insert a permission on an entity. Will have to commit the main
- connection to be considered
- """
- pname = unicode(pname)
- plabel = plabel and unicode(plabel) or unicode(group)
- e = entity.eid
- with security_enabled(self.session, False, False):
- peid = self.execute(
- 'INSERT CWPermission X: X name %(pname)s, X label %(plabel)s,'
- 'X require_group G, E require_permission X '
- 'WHERE G name %(group)s, E eid %(e)s',
- locals())[0][0]
- return peid
-
@contextmanager
def temporary_appobjects(self, *appobjects):
self.vreg._loadedmods.setdefault(self.__module__, {})
@@ -469,6 +495,10 @@
entity.cw_attr_cache.pop('modification_date', None)
self.failUnless(entity.modification_date > olddate)
+ def assertItemsEqual(self, it1, it2, *args, **kwargs):
+ it1 = set(getattr(x, 'eid', x) for x in it1)
+ it2 = set(getattr(x, 'eid', x) for x in it2)
+ super(CubicWebTC, self).assertItemsEqual(it1, it2, *args, **kwargs)
# workflow utilities #######################################################
@@ -581,9 +611,9 @@
return publisher
requestcls = fake.FakeRequest
- def request(self, rollbackfirst=False, **kwargs):
+ def request(self, rollbackfirst=False, url=None, **kwargs):
"""return a web ui request"""
- req = self.requestcls(self.vreg, form=kwargs)
+ req = self.requestcls(self.vreg, url=url, form=kwargs)
if rollbackfirst:
self.websession.cnx.rollback()
req.set_session(self.websession)
@@ -672,7 +702,7 @@
def init_authentication(self, authmode, anonuser=None):
self.set_auth_mode(authmode, anonuser)
- req = self.request()
+ req = self.request(url='login')
origsession = req.session
req.session = req.cnx = None
del req.execute # get back to class implementation
@@ -696,10 +726,11 @@
def assertAuthFailure(self, req, nbsessions=0):
self.app.connect(req)
- self.assertIsInstance(req.session, DBAPISession)
+ self.assertIsInstance(req.session, dbapi.DBAPISession)
self.assertEqual(req.session.cnx, None)
- self.assertEqual(req.cnx, None)
- self.assertEqual(len(self.open_sessions), nbsessions)
+ self.assertIsInstance(req.cnx, (dbapi._NeedAuthAccessMock, NoneType))
+ # + 1 since we should still have session without connection set
+ self.assertEqual(len(self.open_sessions), nbsessions + 1)
clear_cache(req, 'get_authorization')
# content validation #######################################################
--- a/doc/book/en/admin/config.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/admin/config.rst Wed Jul 20 18:21:47 2011 +0200
@@ -190,6 +190,21 @@
db-encoding=utf8
+You need to change the default settings on the database by running::
+
+ ALTER DATABASE <databasename> SET READ_COMMITTED_SNAPSHOT ON;
+
+The ALTER DATABASE command above requires some permissions that your
+user may not have. In that case you will have to ask your local DBA to
+run the query for you.
+
+You can check that the setting is correct by running the following
+query which must return '1'::
+
+ SELECT is_read_committed_snapshot_on
+ FROM sys.databases WHERE name='<databasename>';
+
+
.. _SQLiteConfiguration:
@@ -209,6 +224,8 @@
Pyro configuration
------------------
+Pyro name server
+~~~~~~~~~~~~~~~~
If you want to use Pyro to access your instance remotely, or to have multi-source
or distributed configuration, it is required to have a Pyro name server running
on your network. By default it is detected by a broadcast request, but you can
@@ -216,9 +233,13 @@
To do so, you need to :
+* be sure to have installed it (see :ref:`InstallDependencies`)
+
* launch the pyro name server with `pyro-nsd start` before starting cubicweb
* under debian, edit the file :file:`/etc/default/pyro-nsd` so that the name
server pyro will be launched automatically when the machine fire up
+Note that you can use the pyro server without a running pyro nameserver.
+Refer to `pyro-ns-host` server configuration option for details.
--- a/doc/book/en/admin/index.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/admin/index.rst Wed Jul 20 18:21:47 2011 +0200
@@ -14,6 +14,8 @@
:numbered:
setup
+ setup-windows
+ config
cubicweb-ctl
create-instance
instance-config
--- a/doc/book/en/annexes/faq.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/annexes/faq.rst Wed Jul 20 18:21:47 2011 +0200
@@ -185,12 +185,6 @@
recommended also for other attribute types). By default it expects to generate
HTML, so it deals with rich text formating, xml escaping...
-How do I translate an msg id defined (and translated) in another cube ?
------------------------------------------------------------------------
-
-You should put these translations in the `i18n/static-messages.pot`
-file of your own cube.
-
How to update a database after a schema modification ?
------------------------------------------------------
@@ -380,11 +374,14 @@
You can prefer use a migration script similar to this shell invocation instead::
$ cubicweb-ctl shell <instance>
+ >>> from cubicweb import Binary
>>> from cubicweb.server.utils import crypt_password
>>> crypted = crypt_password('joepass')
>>> rset = rql('Any U WHERE U is CWUser, U login "joe"')
>>> joe = rset.get_entity(0,0)
- >>> joe.set_attributes(upassword=crypted)
+ >>> joe.set_attributes(upassword=Binary(crypted))
+
+Please, refer to the script example is provided in the `misc/examples/chpasswd.py` file.
The more experimented people would use RQL request directly::
--- a/doc/book/en/annexes/rql/debugging.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/annexes/rql/debugging.rst Wed Jul 20 18:21:47 2011 +0200
@@ -8,26 +8,15 @@
Available levels
~~~~~~~~~~~~~~~~
-:DBG_NONE:
- no debug information (current mode)
-
-:DBG_RQL:
- rql execution information
-
-:DBG_SQL:
- executed sql
+Server debugging flags. They may be combined using binary operators.
-:DBG_REPO:
- repository events
-
-:DBG_MS:
- multi-sources
-
-:DBG_MORE:
- more verbosity
-
-:DBG_ALL:
- all level enabled
+.. autodata:: cubicweb.server.DBG_NONE
+.. autodata:: cubicweb.server.DBG_RQL
+.. autodata:: cubicweb.server.DBG_SQL
+.. autodata:: cubicweb.server.DBG_REPO
+.. autodata:: cubicweb.server.DBG_MS
+.. autodata:: cubicweb.server.DBG_MORE
+.. autodata:: cubicweb.server.DBG_ALL
Enable verbose output
@@ -40,6 +29,8 @@
from cubicweb import server
server.set_debug(server.DBG_RQL|server.DBG_SQL|server.DBG_ALL)
+.. autofunction:: cubicweb.server.set_debug
+
Detect largest RQL queries
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -50,7 +41,5 @@
API
~~~
-.. autofunction:: cubicweb.server.set_debug
-
.. autoclass:: cubicweb.server.debugged
--- a/doc/book/en/annexes/rql/intro.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/annexes/rql/intro.rst Wed Jul 20 18:21:47 2011 +0200
@@ -159,3 +159,4 @@
.. _Datalog: http://en.wikipedia.org/wiki/Datalog
.. _intensional: http://en.wikipedia.org/wiki/Intensional_definition
.. _extensional: http://en.wikipedia.org/wiki/Extension_(predicate_logic)
+
--- a/doc/book/en/annexes/rql/language.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/annexes/rql/language.rst Wed Jul 20 18:21:47 2011 +0200
@@ -152,6 +152,10 @@
- Aggregate Functions: COUNT, MIN, MAX, AVG, SUM, GROUP_CONCAT
+.. note::
+ Aggregate functions will return None if there is no result row.
+
+
Having
```````
--- a/doc/book/en/devrepo/vreg.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devrepo/vreg.rst Wed Jul 20 18:21:47 2011 +0200
@@ -79,7 +79,7 @@
.. autoclass:: cubicweb.selectors.has_add_permission
.. autoclass:: cubicweb.selectors.has_mimetype
.. autoclass:: cubicweb.selectors.is_in_state
-.. autoclass:: cubicweb.selectors.on_transition
+.. autofunction:: cubicweb.selectors.on_fire_transition
.. autoclass:: cubicweb.selectors.implements
--- a/doc/book/en/devweb/controllers.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/controllers.rst Wed Jul 20 18:21:47 2011 +0200
@@ -16,11 +16,11 @@
`Browsing`:
* the View controller is associated with most browsing actions within a
- CubicWeb application: it always instantiates a :ref:`the_main_template` and
- lets the ResultSet/Views dispatch system build up the whole content; it
- handles :exc:`ObjectNotFound` and :exc:`NoSelectableObject` errors that may
- bubble up to its entry point, in an end-user-friendly way (but other
- programming errors will slip through)
+ CubicWeb application: it always instantiates a
+ :ref:`the_main_template_layout` and lets the ResultSet/Views dispatch system
+ build up the whole content; it handles :exc:`ObjectNotFound` and
+ :exc:`NoSelectableObject` errors that may bubble up to its entry point, in an
+ end-user-friendly way (but other programming errors will slip through)
* the JSon controller (same module) provides services for Ajax calls,
typically using JSON as a serialization format for input, and
--- a/doc/book/en/devweb/httpcaching.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/httpcaching.rst Wed Jul 20 18:21:47 2011 +0200
@@ -1,3 +1,21 @@
HTTP cache management
----------------------
-XXX feedme
\ No newline at end of file
+=====================
+
+.. automodule:: cubicweb.web.httpcache
+
+Cache policies
+--------------
+.. autoclass:: cubicweb.web.httpcache.NoHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.MaxAgeHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EtagHTTPCacheManager
+.. autoclass:: cubicweb.web.httpcache.EntityHTTPCacheManager
+
+Exception
+---------
+.. autoexception:: cubicweb.web.httpcache.NoEtag
+
+Helper functions
+----------------
+.. autofunction:: cubicweb.web.httpcache.set_http_cache_headers
+
+.. NOT YET AVAILABLE IN STABLE autofunction:: cubicweb.web.httpcache.lastmodified
--- a/doc/book/en/devweb/index.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/index.rst Wed Jul 20 18:21:47 2011 +0200
@@ -17,5 +17,6 @@
edition/index
facets
internationalization
-.. property
+ property
httpcaching
+ resource
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/devweb/resource.rst Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,16 @@
+Locate resources
+----------------
+
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_resource
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_doc_file
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.locate_all_files
+
+Static files handling
+---------------------
+
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_directory
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_exists
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_open
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_add
+.. automethod:: cubicweb.web.webconfig.WebConfiguration.static_file_del
+
--- a/doc/book/en/devweb/views/idownloadable.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/views/idownloadable.rst Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,23 @@
-The 'download' view
--------------------
+The 'download' views
+====================
+
+.. automodule:: cubicweb.web.views.idownloadable
+
+Components
+----------
+
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadBox
-(:mod:`cubicweb.web.views.idownloadable`)
+Download views
+--------------
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadView
+.. autoclass:: cubicweb.web.views.idownloadable.DownloadLinkView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadablePrimaryView
+.. autoclass:: cubicweb.web.views.idownloadable.IDownloadableLineView
+
+Embedded views
+--------------
+
+.. autoclass:: cubicweb.web.views.idownloadable.ImageView
+.. autoclass:: cubicweb.web.views.idownloadable.EHTMLView
--- a/doc/book/en/devweb/views/index.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/views/index.rst Wed Jul 20 18:21:47 2011 +0200
@@ -25,7 +25,7 @@
urlpublish
breadcrumbs
-.. wdoc
+ idownloadable
+ wdoc
.. embedding
-.. idownloadable
--- a/doc/book/en/devweb/views/wdoc.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/devweb/views/wdoc.rst Wed Jul 20 18:21:47 2011 +0200
@@ -1,9 +1,17 @@
.. -*- coding: utf-8 -*-
Online documentation system
----------------------------
+===========================
+
+.. automodule:: cubicweb.web.views.wdoc
-(:mod:`cubicweb.web.views.wdoc`)
+Help views
+----------
+.. autoclass:: cubicweb.web.views.wdoc.InlineHelpView
+.. autoclass:: cubicweb.web.views.wdoc.ChangeLogView
-XXX describe the on-line documentation system
-
+Actions
+-------
+.. autoclass:: cubicweb.web.views.wdoc.HelpAction
+.. autoclass:: cubicweb.web.views.wdoc.ChangeLogAction
+.. autoclass:: cubicweb.web.views.wdoc.AboutAction
--- a/doc/book/en/tutorials/advanced/part03_bfss.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/tutorials/advanced/part03_bfss.rst Wed Jul 20 18:21:47 2011 +0200
@@ -32,7 +32,7 @@
makedirs(bfssdir)
print 'created', bfssdir
storage = storages.BytesFileSystemStorage(bfssdir)
- set_attribute_storage(self.repo, 'File', 'data', storage)
+ storages.set_attribute_storage(self.repo, 'File', 'data', storage)
.. Note::
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Wed Jul 20 18:21:47 2011 +0200
@@ -3,6 +3,8 @@
We'll now see how to benefit from features introduced in 3.9 and 3.10 releases of CubicWeb
+.. _uiprops:
+
Step 1: tired of the default look?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- a/doc/book/en/tutorials/tools/windmill.rst Mon May 16 16:24:00 2011 +0200
+++ b/doc/book/en/tutorials/tools/windmill.rst Wed Jul 20 18:21:47 2011 +0200
@@ -23,23 +23,21 @@
environment, take a look to the `virtualenv
<http://pypi.python.org/pypi/virtualenv>`_ project as well)::
- pip install windmill
- curl -O http://github.com/windmill/windmill/tarball/master
+ $ pip install windmill
+ $ curl -O http://github.com/windmill/windmill/tarball/master
However, the Windmill project doesn't release frequently. Our recommandation is
-to used the last snapshot of the Git repository:
-
-.. sourcecode:: bash
+to used the last snapshot of the Git repository::
- git clone git://github.com/windmill/windmill.git HEAD
- cd windmill
- python setup.py develop
+ $ git clone git://github.com/windmill/windmill.git HEAD
+ $ cd windmill
+ $ python setup.py develop
Install instructions are `available <http://wiki.github.com/windmill/windmill/installing>`_.
Be sure to have the windmill module in your PYTHONPATH afterwards::
- python -c "import windmill"
+ $ python -c "import windmill"
X dummy
-------
@@ -60,9 +58,9 @@
*From: http://www.x.org/wiki/XorgTesting*
-Then, you can run the X server with the following command :
+Then, you can run the X server with the following command ::
- /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
+ $ /usr/bin/X11/Xvfb :1 -ac -screen 0 1280x1024x8 -fbdir /tmp
Windmill usage
@@ -82,13 +80,15 @@
If you are using firefox as client, consider the "firebug" option.
-If you have a running instance, you can refine the test by the *loadtest* windmill option:
+If you have a running instance, you can refine the test by the *loadtest* windmill option::
- windmill -m firebug loadtest=<test_file.py> <instance url>
+ $ windmill -m firebug loadtest=<test_file.py> <instance url>
-Or use the internal windmill shell to explore available commands:
+Or use the internal windmill shell to explore available commands::
- windmill -m firebug shell <instance url>
+ $ windmill -m firebug shell <instance url>
+
+And enter python commands:
.. sourcecode:: python
@@ -125,7 +125,7 @@
To run your test series::
- % pytest test/test_windmill.py
+ $ pytest test/test_windmill.py
By default, CubicWeb will use **firefox** as the default browser and will try
to run test instance server on localhost. In the general case, You've no need
@@ -144,6 +144,8 @@
Examples:
+.. sourcecode:: python
+
browser = 'firefox'
test_dir = osp.join(__file__, 'windmill')
edit_test = False
@@ -162,7 +164,7 @@
For instance, CubicWeb framework windmill tests can be manually run by::
- % pytest web/test/test_windmill.py
+ $ pytest web/test/test_windmill.py
Edit your tests
---------------
@@ -172,7 +174,7 @@
But if you are using `pytest` as test runner, use the `-i` option directly.
The test series will be loaded and you can run assertions step-by-step::
- % pytest -i test/test_windmill.py
+ $ pytest -i test/test_windmill.py
In this case, the `firebug` extension will be loaded automatically for you.
--- a/doc/tools/pyjsrest.py Mon May 16 16:24:00 2011 +0200
+++ b/doc/tools/pyjsrest.py Wed Jul 20 18:21:47 2011 +0200
@@ -127,8 +127,6 @@
'cubicweb.htmlhelpers',
'cubicweb.ajax',
- 'cubicweb.lazy',
- 'cubicweb.tabs',
'cubicweb.ajax.box',
'cubicweb.facets',
'cubicweb.widgets',
@@ -153,9 +151,6 @@
'jquery.flot.js',
'jquery.corner.js',
'jquery.ui.js',
- 'ui.core.js',
- 'ui.tabs.js',
- 'ui.slider.js',
'excanvas.js',
'gmap.utility.labeledmarker.js',
--- a/entities/authobjs.py Mon May 16 16:24:00 2011 +0200
+++ b/entities/authobjs.py Wed Jul 20 18:21:47 2011 +0200
@@ -29,9 +29,21 @@
fetch_attrs, fetch_order = fetch_config(['name'])
fetch_unrelated_order = fetch_order
- def db_key_name(self):
- """XXX goa specific"""
- return self.get('name')
+ def grant_permission(self, entity, pname, plabel=None):
+ """grant local `pname` permission on `entity` to this group using
+ :class:`CWPermission`.
+
+ If a similar permission already exists, add the group to it, else create
+ a new one.
+ """
+ if not self._cw.execute(
+ 'SET X require_group G WHERE E eid %(e)s, G eid %(g)s, '
+ 'E require_permission X, X name %(name)s, X label %(label)s',
+ {'e': entity.eid, 'g': self.eid,
+ 'name': pname, 'label': plabel}):
+ self._cw.create_entity('CWPermission', name=pname, label=plabel,
+ require_group=self,
+ reverse_require_permission=entity)
class CWUser(AnyEntity):
@@ -156,10 +168,6 @@
dc_long_title = name
- def db_key_name(self):
- """XXX goa specific"""
- return self.get('login')
-
from logilab.common.deprecation import class_renamed
EUser = class_renamed('EUser', CWUser)
EGroup = class_renamed('EGroup', CWGroup)
--- a/entities/schemaobjs.py Mon May 16 16:24:00 2011 +0200
+++ b/entities/schemaobjs.py Wed Jul 20 18:21:47 2011 +0200
@@ -45,10 +45,6 @@
return u'%s <<%s>>' % (self.dc_title(), ', '.join(stereotypes))
return self.dc_title()
- def db_key_name(self):
- """XXX goa specific"""
- return self.get('name')
-
class CWRType(AnyEntity):
__regid__ = 'CWRType'
@@ -87,10 +83,6 @@
"has cardinality=%(card)s")
raise ValidationError(self.eid, {qname: msg % locals()})
- def db_key_name(self):
- """XXX goa specific"""
- return self.get('name')
-
class CWRelation(AnyEntity):
__regid__ = 'CWRelation'
--- a/entities/test/unittest_base.py Mon May 16 16:24:00 2011 +0200
+++ b/entities/test/unittest_base.py Wed Jul 20 18:21:47 2011 +0200
@@ -34,7 +34,8 @@
class BaseEntityTC(CubicWebTC):
def setup_database(self):
- self.member = self.create_user('member')
+ req = self.request()
+ self.member = self.create_user(req, 'member')
--- a/entities/test/unittest_wfobjs.py Mon May 16 16:24:00 2011 +0200
+++ b/entities/test/unittest_wfobjs.py Wed Jul 20 18:21:47 2011 +0200
@@ -95,13 +95,15 @@
class WorkflowTC(CubicWebTC):
def setup_database(self):
+ req = self.request()
rschema = self.schema['in_state']
for rdef in rschema.rdefs.values():
self.assertEqual(rdef.cardinality, '1*')
- self.member = self.create_user('member')
+ self.member = self.create_user(req, 'member')
def test_workflow_base(self):
- e = self.create_user('toto')
+ req = self.request()
+ e = self.create_user(req, 'toto')
iworkflowable = e.cw_adapt_to('IWorkflowable')
self.assertEqual(iworkflowable.state, 'activated')
iworkflowable.change_state('deactivated', u'deactivate 1')
@@ -170,13 +172,14 @@
self.assertEqual(trinfo.transition.name, 'deactivate')
def test_goback_transition(self):
+ req = self.request()
wf = self.session.user.cw_adapt_to('IWorkflowable').current_workflow
asleep = wf.add_state('asleep')
wf.add_transition('rest', (wf.state_by_name('activated'),
wf.state_by_name('deactivated')),
asleep)
wf.add_transition('wake up', asleep)
- user = self.create_user('stduser')
+ user = self.create_user(req, 'stduser')
iworkflowable = user.cw_adapt_to('IWorkflowable')
iworkflowable.fire_transition('rest')
self.commit()
@@ -196,7 +199,8 @@
def _test_stduser_deactivate(self):
ueid = self.member.eid
- self.create_user('tutu')
+ req = self.request()
+ self.create_user(req, 'tutu')
cnx = self.login('tutu')
req = self.request()
iworkflowable = req.entity_from_eid(self.member.eid).cw_adapt_to('IWorkflowable')
@@ -393,7 +397,8 @@
class CustomWorkflowTC(CubicWebTC):
def setup_database(self):
- self.member = self.create_user('member')
+ req = self.request()
+ self.member = self.create_user(req, 'member')
def test_custom_wf_replace_state_no_history(self):
"""member in inital state with no previous history, state is simply
@@ -493,7 +498,8 @@
def test_auto_transition_fired(self):
wf = self.setup_custom_wf()
- user = self.create_user('member')
+ req = self.request()
+ user = self.create_user(req, 'member')
iworkflowable = user.cw_adapt_to('IWorkflowable')
self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s',
{'wf': wf.eid, 'x': user.eid})
@@ -523,7 +529,8 @@
def test_auto_transition_custom_initial_state_fired(self):
wf = self.setup_custom_wf()
- user = self.create_user('member', surname=u'toto')
+ req = self.request()
+ user = self.create_user(req, 'member', surname=u'toto')
self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s',
{'wf': wf.eid, 'x': user.eid})
self.commit()
@@ -538,7 +545,8 @@
type=u'auto', conditions=({'expr': u'X surname "toto"',
'mainvars': u'X'},))
self.commit()
- user = self.create_user('member', surname=u'toto')
+ req = self.request()
+ user = self.create_user(req, 'member', surname=u'toto')
self.commit()
iworkflowable = user.cw_adapt_to('IWorkflowable')
self.assertEqual(iworkflowable.state, 'dead')
@@ -554,7 +562,8 @@
self.s_deactivated = self.wf.state_by_name('deactivated').eid
self.s_dummy = self.wf.add_state(u'dummy').eid
self.wf.add_transition(u'dummy', (self.s_deactivated,), self.s_dummy)
- ueid = self.create_user('stduser', commit=False).eid
+ req = self.request()
+ ueid = self.create_user(req, 'stduser', commit=False).eid
# test initial state is set
rset = self.execute('Any N WHERE S name N, X in_state S, X eid %(x)s',
{'x' : ueid})
--- a/entities/wfobjs.py Mon May 16 16:24:00 2011 +0200
+++ b/entities/wfobjs.py Wed Jul 20 18:21:47 2011 +0200
@@ -572,18 +572,31 @@
kwargs['to_state'] = self._cw.entity_from_eid(tseid)
return self._cw.create_entity('TrInfo', **kwargs)
- def fire_transition(self, tr, comment=None, commentformat=None):
- """change the entity's state by firing transition of the given name in
- entity's workflow
- """
+ def _get_transition(self, tr):
assert self.current_workflow
if isinstance(tr, basestring):
_tr = self.current_workflow.transition_by_name(tr)
assert _tr is not None, 'not a %s transition: %s' % (
self.__regid__, tr)
tr = _tr
+ return tr
+
+ def fire_transition(self, tr, comment=None, commentformat=None):
+ """change the entity's state by firing given transition (name or entity)
+ in entity's workflow
+ """
+ tr = self._get_transition(tr)
return self._add_trinfo(comment, commentformat, tr.eid)
+ def fire_transition_if_possible(self, tr, comment=None, commentformat=None):
+ """change the entity's state by firing given transition (name or entity)
+ in entity's workflow if this transition is possible
+ """
+ tr = self._get_transition(tr)
+ if any(tr_ for tr_ in self.possible_transitions()
+ if tr_.eid == tr.eid):
+ self.fire_transition(tr)
+
def change_state(self, statename, comment=None, commentformat=None, tr=None):
"""change the entity's state to the given state (name or entity) in
entity's workflow. This method should only by used by manager to fix an
@@ -595,7 +608,7 @@
stateeid = statename.eid
else:
if not isinstance(statename, basestring):
- warn('[3.5] give a state name', DeprecationWarning)
+ warn('[3.5] give a state name', DeprecationWarning, stacklevel=2)
state = self.current_workflow.state_by_eid(statename)
else:
state = self.current_workflow.state_by_name(statename)
@@ -605,3 +618,20 @@
stateeid = state.eid
# XXX try to find matching transition?
return self._add_trinfo(comment, commentformat, tr and tr.eid, stateeid)
+
+ def set_initial_state(self, statename):
+ """set a newly created entity's state to the given state (name or entity)
+ in entity's workflow. This is useful if you don't want it to be the
+ workflow's initial state.
+ """
+ assert self.current_workflow
+ if hasattr(statename, 'eid'):
+ stateeid = statename.eid
+ else:
+ state = self.current_workflow.state_by_name(statename)
+ if state is None:
+ raise WorkflowException('not a %s state: %s' % (self.__regid__,
+ statename))
+ stateeid = state.eid
+ self._cw.execute('SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
+ {'x': self.entity.eid, 's': stateeid})
--- a/entity.py Mon May 16 16:24:00 2011 +0200
+++ b/entity.py Wed Jul 20 18:21:47 2011 +0200
@@ -28,7 +28,7 @@
from rql.utils import rqlvar_maker
-from cubicweb import Unauthorized, typed_eid
+from cubicweb import Unauthorized, typed_eid, neg_role
from cubicweb.rset import ResultSet
from cubicweb.selectors import yes
from cubicweb.appobject import AppObject
@@ -62,6 +62,23 @@
return True
+def remove_ambiguous_rels(attr_set, subjtypes, schema):
+ '''remove from `attr_set` the relations of entity types `subjtypes` that have
+ different entity type sets as target'''
+ for attr in attr_set.copy():
+ rschema = schema.rschema(attr)
+ if rschema.final:
+ continue
+ ttypes = None
+ for subjtype in subjtypes:
+ cur_ttypes = rschema.objects(subjtype)
+ if ttypes is None:
+ ttypes = cur_ttypes
+ elif cur_ttypes != ttypes:
+ attr_set.remove(attr)
+ break
+
+
class Entity(AppObject):
"""an entity instance has e_schema automagically set on
the class and instances has access to their issuing cursor.
@@ -91,7 +108,7 @@
# class attributes that must be set in class definition
rest_attr = None
fetch_attrs = None
- skip_copy_for = ('in_state',)
+ skip_copy_for = ('in_state',) # XXX turn into a set
# class attributes set automatically at registration time
e_schema = None
@@ -157,6 +174,7 @@
def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X',
settype=True, ordermethod='fetch_order'):
"""return a rql to fetch all entities of the class type"""
+ # XXX update api and implementation to AST manipulation (see unrelated rql)
restrictions = restriction or []
if settype:
restrictions.append('%s is %s' % (mainvar, cls.__regid__))
@@ -165,6 +183,7 @@
selection = [mainvar]
orderby = []
# start from 26 to avoid possible conflicts with X
+ # XXX not enough to be sure it'll be no conflicts
varmaker = rqlvar_maker(index=26)
cls._fetch_restrictions(mainvar, varmaker, fetchattrs, selection,
orderby, restrictions, user, ordermethod)
@@ -202,8 +221,6 @@
restriction = '%s %s %s' % (mainvar, attr, var)
restrictions.append(restriction)
if not rschema.final:
- # XXX this does not handle several destination types
- desttype = rschema.objects(eschema.type)[0]
card = rdef.cardinality[0]
if card not in '?1':
cls.warning('bad relation %s specified in fetch attrs for %s',
@@ -216,11 +233,18 @@
# that case the relation may still be missing. As we miss this
# later information here, systematically add it.
restrictions[-1] += '?'
+ targettypes = rschema.objects(eschema.type)
# XXX user._cw.vreg iiiirk
- destcls = user._cw.vreg['etypes'].etype_class(desttype)
- destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs,
- selection, orderby, restrictions,
- user, ordermethod, visited=visited)
+ etypecls = user._cw.vreg['etypes'].etype_class(targettypes[0])
+ if len(targettypes) > 1:
+ # find fetch_attrs common to all destination types
+ fetchattrs = user._cw.vreg['etypes'].fetch_attrs(targettypes)
+ remove_ambiguous_rels(fetchattrs, targettypes, user._cw.vreg.schema)
+ else:
+ fetchattrs = etypecls.fetch_attrs
+ etypecls._fetch_restrictions(var, varmaker, fetchattrs,
+ selection, orderby, restrictions,
+ user, ordermethod, visited=visited)
if ordermethod is not None:
orderterm = getattr(cls, ordermethod)(attr, var)
if orderterm:
@@ -264,6 +288,7 @@
restrictions = set()
pending_relations = []
eschema = cls.e_schema
+ qargs = {}
for attr, value in kwargs.items():
if attr.startswith('reverse_'):
attr = attr[len('reverse_'):]
@@ -277,10 +302,11 @@
value = iter(value).next()
else:
# prepare IN clause
- del kwargs[attr]
- pending_relations.append( (attr, value) )
+ pending_relations.append( (attr, role, value) )
continue
- if hasattr(value, 'eid'): # non final relation
+ if rschema.final: # attribute
+ relations.append('X %s %%(%s)s' % (attr, attr))
+ else:
rvar = attr.upper()
if role == 'object':
relations.append('%s %s X' % (rvar, attr))
@@ -289,21 +315,21 @@
restriction = '%s eid %%(%s)s' % (rvar, attr)
if not restriction in restrictions:
restrictions.add(restriction)
- kwargs[attr] = value.eid
- else: # attribute
- relations.append('X %s %%(%s)s' % (attr, attr))
+ if hasattr(value, 'eid'):
+ value = value.eid
+ qargs[attr] = value
if relations:
rql = '%s: %s' % (rql, ', '.join(relations))
if restrictions:
rql = '%s WHERE %s' % (rql, ', '.join(restrictions))
- created = execute(rql, kwargs).get_entity(0, 0)
- for attr, values in pending_relations:
- if attr.startswith('reverse_'):
- restr = 'Y %s X' % attr[len('reverse_'):]
+ created = execute(rql, qargs).get_entity(0, 0)
+ for attr, role, values in pending_relations:
+ if role == 'object':
+ restr = 'Y %s X' % attr
else:
restr = 'X %s Y' % attr
execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
- restr, ','.join(str(r.eid) for r in values)),
+ restr, ','.join(str(getattr(r, 'eid', r)) for r in values)),
{'x': created.eid}, build_descr=False)
return created
@@ -728,17 +754,17 @@
else:
restriction += ', X is IN (%s)' % ','.join(targettypes)
card = greater_card(rschema, targettypes, (self.e_schema,), 1)
+ etypecls = self._cw.vreg['etypes'].etype_class(targettypes[0])
if len(targettypes) > 1:
- fetchattrs_list = []
- for ttype in targettypes:
- etypecls = self._cw.vreg['etypes'].etype_class(ttype)
- fetchattrs_list.append(set(etypecls.fetch_attrs))
- fetchattrs = reduce(set.intersection, fetchattrs_list)
- rql = etypecls.fetch_rql(self._cw.user, [restriction], fetchattrs,
- settype=False)
+ fetchattrs = self._cw.vreg['etypes'].fetch_attrs(targettypes)
+ # XXX we should fetch ambiguous relation objects too but not
+ # recurse on them in _fetch_restrictions; it is easier to remove
+ # them completely for now, as it would require an deeper api rewrite
+ remove_ambiguous_rels(fetchattrs, targettypes, self._cw.vreg.schema)
else:
- etypecls = self._cw.vreg['etypes'].etype_class(targettypes[0])
- rql = etypecls.fetch_rql(self._cw.user, [restriction], settype=False)
+ fetchattrs = etypecls.fetch_attrs
+ rql = etypecls.fetch_rql(self._cw.user, [restriction], fetchattrs,
+ settype=False)
# optimisation: remove ORDERBY if cardinality is 1 or ? (though
# greater_card return 1 for those both cases)
if card == '1':
@@ -762,7 +788,7 @@
# generic vocabulary methods ##############################################
def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None,
- vocabconstraints=True):
+ vocabconstraints=True):
"""build a rql to fetch `targettype` entities unrelated to this entity
using (rtype, role) relation.
@@ -772,58 +798,83 @@
ordermethod = ordermethod or 'fetch_unrelated_order'
if isinstance(rtype, basestring):
rtype = self._cw.vreg.schema.rschema(rtype)
+ rdef = rtype.role_rdef(self.e_schema, targettype, role)
+ rewriter = RQLRewriter(self._cw)
+ # initialize some variables according to the `role` of `self` in the
+ # relation:
+ # * variable for myself (`evar`) and searched entities (`searchvedvar`)
+ # * entity type of the subject (`subjtype`) and of the object
+ # (`objtype`) of the relation
if role == 'subject':
evar, searchedvar = 'S', 'O'
subjtype, objtype = self.e_schema, targettype
else:
searchedvar, evar = 'S', 'O'
objtype, subjtype = self.e_schema, targettype
+ # initialize some variables according to `self` existance
+ if rdef.role_cardinality(neg_role(role)) in '?1':
+ # if cardinality in '1?', we want a target entity which isn't
+ # already linked using this relation
+ if searchedvar == 'S':
+ restriction = ['NOT S %s ZZ' % rtype]
+ else:
+ restriction = ['NOT ZZ %s O' % rtype]
+ elif self.has_eid():
+ # elif we have an eid, we don't want a target entity which is
+ # already linked to ourself through this relation
+ restriction = ['NOT S %s O' % rtype]
+ else:
+ restriction = []
if self.has_eid():
- restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar]
+ restriction += ['%s eid %%(x)s' % evar]
args = {'x': self.eid}
if role == 'subject':
- securitycheck_args = {'fromeid': self.eid}
+ sec_check_args = {'fromeid': self.eid}
else:
- securitycheck_args = {'toeid': self.eid}
+ sec_check_args = {'toeid': self.eid}
+ existant = None # instead of 'SO', improve perfs
else:
- restriction = []
args = {}
- securitycheck_args = {}
- rdef = rtype.role_rdef(self.e_schema, targettype, role)
- insertsecurity = (rdef.has_local_role('add') and not
- rdef.has_perm(self._cw, 'add', **securitycheck_args))
- # XXX consider constraint.mainvars to check if constraint apply
+ sec_check_args = {}
+ existant = searchedvar
+ # retreive entity class for targettype to compute base rql
+ etypecls = self._cw.vreg['etypes'].etype_class(targettype)
+ rql = etypecls.fetch_rql(self._cw.user, restriction,
+ mainvar=searchedvar, ordermethod=ordermethod)
+ select = self._cw.vreg.parse(self._cw, rql, args).children[0]
+ # insert RQL expressions for schema constraints into the rql syntax tree
if vocabconstraints:
# RQLConstraint is a subclass for RQLVocabularyConstraint, so they
# will be included as well
- restriction += [cstr.restriction for cstr in rdef.constraints
- if isinstance(cstr, RQLVocabularyConstraint)]
+ cstrcls = RQLVocabularyConstraint
else:
- restriction += [cstr.restriction for cstr in rdef.constraints
- if isinstance(cstr, RQLConstraint)]
- etypecls = self._cw.vreg['etypes'].etype_class(targettype)
- rql = etypecls.fetch_rql(self._cw.user, restriction,
- mainvar=searchedvar, ordermethod=ordermethod)
+ cstrcls = RQLConstraint
+ for cstr in rdef.constraints:
+ # consider constraint.mainvars to check if constraint apply
+ if isinstance(cstr, cstrcls) and searchedvar in cstr.mainvars:
+ if not self.has_eid() and evar in cstr.mainvars:
+ continue
+ # compute a varmap suitable to RQLRewriter.rewrite argument
+ varmap = dict((v, v) for v in 'SO' if v in select.defined_vars
+ and v in cstr.mainvars)
+ # rewrite constraint by constraint since we want a AND between
+ # expressions.
+ rewriter.rewrite(select, [(varmap, (cstr,))], select.solutions,
+ args, existant)
+ # insert security RQL expressions granting the permission to 'add' the
+ # relation into the rql syntax tree, if necessary
+ rqlexprs = rdef.get_rqlexprs('add')
+ if rqlexprs and not rdef.has_perm(self._cw, 'add', **sec_check_args):
+ # compute a varmap suitable to RQLRewriter.rewrite argument
+ varmap = dict((v, v) for v in 'SO' if v in select.defined_vars)
+ # rewrite all expressions at once since we want a OR between them.
+ rewriter.rewrite(select, [(varmap, rqlexprs)], select.solutions,
+ args, existant)
# ensure we have an order defined
- if not ' ORDERBY ' in rql:
- before, after = rql.split(' WHERE ', 1)
- rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
- if insertsecurity:
- rqlexprs = rdef.get_rqlexprs('add')
- rewriter = RQLRewriter(self._cw)
- rqlst = self._cw.vreg.parse(self._cw, rql, args)
- if not self.has_eid():
- existant = searchedvar
- else:
- existant = None # instead of 'SO', improve perfs
- for select in rqlst.children:
- varmap = {}
- for var in 'SO':
- if var in select.defined_vars:
- varmap[var] = var
- rewriter.rewrite(select, [(varmap, rqlexprs)],
- select.solutions, args, existant)
- rql = rqlst.as_string()
+ if not select.orderby:
+ select.add_sort_var(select.defined_vars[searchedvar])
+ # we're done, turn the rql syntax tree as a string
+ rql = select.as_string()
return rql, args
def unrelated(self, rtype, targettype, role='subject', limit=None,
@@ -835,6 +886,7 @@
rql, args = self.cw_unrelated_rql(rtype, targettype, role, ordermethod)
except Unauthorized:
return self._cw.empty_rset()
+ # XXX should be set in unrelated rql when manipulating the AST
if limit is not None:
before, after = rql.split(' WHERE ', 1)
rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
@@ -930,8 +982,9 @@
"""add relations to the given object. To set a relation where this entity
is the object of the relation, use 'reverse_'<relation> as argument name.
- Values may be an entity, a list of entities, or None (meaning that all
- relations of the given type from or to this object should be deleted).
+ Values may be an entity or eid, a list of entities or eids, or None
+ (meaning that all relations of the given type from or to this object
+ should be deleted).
"""
# XXX update cache
_check_cw_unsafe(kwargs)
@@ -946,9 +999,17 @@
continue
if not isinstance(values, (tuple, list, set, frozenset)):
values = (values,)
+ eids = []
+ for val in values:
+ try:
+ eids.append(str(val.eid))
+ except AttributeError:
+ try:
+ eids.append(str(typed_eid(val)))
+ except (ValueError, TypeError):
+ raise Exception('expected an Entity or eid, got %s' % val)
self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
- restr, ','.join(str(r.eid) for r in values)),
- {'x': self.eid})
+ restr, ','.join(eids)), {'x': self.eid})
def cw_delete(self, **kwargs):
assert self.has_eid(), self.eid
--- a/hooks/integrity.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/integrity.py Wed Jul 20 18:21:47 2011 +0200
@@ -121,14 +121,15 @@
return
session = self._cw
eidfrom, eidto = self.eidfrom, self.eidto
- pendingrdefs = session.transaction_data.get('pendingrdefs', ())
- if (session.describe(eidfrom)[0], rtype, session.describe(eidto)[0]) in pendingrdefs:
+ rdef = session.rtype_eids_rdef(rtype, eidfrom, eidto)
+ if (rdef.subject, rtype, rdef.object) in session.transaction_data.get('pendingrdefs', ()):
return
- card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
+ card = rdef.cardinality
if card[0] in '1+' and not session.deleted_in_transaction(eidfrom):
- _CheckSRelationOp.get_instance(self._cw).add_data((eidfrom, rtype))
+ _CheckSRelationOp.get_instance(session).add_data((eidfrom, rtype))
if card[1] in '1+' and not session.deleted_in_transaction(eidto):
- _CheckORelationOp.get_instance(self._cw).add_data((eidto, rtype))
+ _CheckORelationOp.get_instance(session).add_data((eidto, rtype))
+
class CheckCardinalityHookAfterAddEntity(IntegrityHook):
"""check cardinalities are satisfied"""
@@ -150,21 +151,6 @@
op = _CheckORelationOp.get_instance(self._cw)
op.add_data((eid, rschema.type))
- def before_delete_relation(self):
- rtype = self.rtype
- if rtype in DONT_CHECK_RTYPES_ON_DEL:
- return
- session = self._cw
- eidfrom, eidto = self.eidfrom, self.eidto
- pendingrdefs = session.transaction_data.get('pendingrdefs', ())
- if (session.describe(eidfrom)[0], rtype, session.describe(eidto)[0]) in pendingrdefs:
- return
- card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
- if card[0] in '1+' and not session.deleted_in_transaction(eidfrom):
- _CheckSRelationOp.get_instance(self._cw).add_data((eidfrom, rtype))
- if card[1] in '1+' and not session.deleted_in_transaction(eidto):
- _CheckORelationOp.get_instance(self._cw).add_data((eidto, rtype))
-
class _CheckConstraintsOp(hook.DataOperationMixIn, hook.LateOperation):
""" check a new relation satisfy its constraints """
@@ -204,8 +190,8 @@
def __call__(self):
# XXX get only RQL[Unique]Constraints?
- constraints = self._cw.schema_rproperty(self.rtype, self.eidfrom, self.eidto,
- 'constraints')
+ rdef = self._cw.rtype_eids_rdef(self.rtype, self.eidfrom, self.eidto)
+ constraints = rdef.constraints
if constraints:
_CheckConstraintsOp.get_instance(self._cw).add_data(
(self.eidfrom, self.rtype, self.eidto, constraints))
@@ -355,15 +341,15 @@
def __call__(self):
# if the relation is being delete, don't delete composite's components
# automatically
- pendingrdefs = self._cw.transaction_data.get('pendingrdefs', ())
- if (self._cw.describe(self.eidfrom)[0], self.rtype,
- self._cw.describe(self.eidto)[0]) in pendingrdefs:
+ session = self._cw
+ rtype = self.rtype
+ rdef = session.rtype_eids_rdef(rtype, self.eidfrom, self.eidto)
+ if (rdef.subject, rtype, rdef.object) in session.transaction_data.get('pendingrdefs', ()):
return
- composite = self._cw.schema_rproperty(self.rtype, self.eidfrom, self.eidto,
- 'composite')
+ composite = rdef.composite
if composite == 'subject':
_DelayedDeleteOEntityOp.get_instance(self._cw).add_data(
- (self.eidto, self.rtype))
+ (self.eidto, rtype))
elif composite == 'object':
_DelayedDeleteSEntityOp.get_instance(self._cw).add_data(
- (self.eidfrom, self.rtype))
+ (self.eidfrom, rtype))
--- a/hooks/metadata.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/metadata.py Wed Jul 20 18:21:47 2011 +0200
@@ -67,13 +67,12 @@
def precommit_event(self):
session = self.session
- for eid in self.get_data():
- if session.deleted_in_transaction(eid):
- # entity have been created and deleted in the same transaction
- continue
- entity = session.entity_from_eid(eid)
- if not entity.created_by:
- session.add_relation(eid, 'created_by', session.user.eid)
+ relations = [(eid, session.user.eid) for eid in self.get_data()
+ # don't consider entities that have been created and
+ # deleted in the same transaction
+ if not session.deleted_in_transaction(eid) and \
+ not session.entity_from_eid(eid).created_by]
+ session.add_relations([('created_by', relations)])
class SetOwnershipHook(MetaDataHook):
@@ -107,7 +106,7 @@
# skip this special composite relation # XXX (syt) why?
return
eidfrom, eidto = self.eidfrom, self.eidto
- composite = self._cw.schema_rproperty(self.rtype, eidfrom, eidto, 'composite')
+ composite = self._cw.rtype_eids_rdef(self.rtype, eidfrom, eidto).composite
if composite == 'subject':
SyncOwnersOp.get_instance(self._cw).add_data( (eidfrom, eidto) )
elif composite == 'object':
--- a/hooks/syncschema.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/syncschema.py Wed Jul 20 18:21:47 2011 +0200
@@ -263,7 +263,7 @@
for rtype in (META_RTYPES - VIRTUAL_RTYPES):
try:
rschema = schema[rtype]
- except:
+ except KeyError:
if rtype == 'cw_source':
continue # XXX 3.10 migration
raise
@@ -574,7 +574,8 @@
elif lastrel:
DropRelationTable(session, str(rschema))
# then update the in-memory schema
- rschema.del_relation_def(rdef.subject, rdef.object)
+ if rdef.subject not in ETYPE_NAME_MAP and rdef.object not in ETYPE_NAME_MAP:
+ rschema.del_relation_def(rdef.subject, rdef.object)
# if this is the last relation definition of this type, drop associated
# relation type
if lastrel and not session.deleted_in_transaction(rschema.eid):
@@ -585,8 +586,10 @@
#
# Note: add_relation_def takes a RelationDefinition, not a
# RelationDefinitionSchema, needs to fake it
- self.rdef.name = str(self.rdef.rtype)
- self.session.vreg.schema.add_relation_def(self.rdef)
+ rdef = self.rdef
+ rdef.name = str(rdef.rtype)
+ if rdef.subject not in ETYPE_NAME_MAP and rdef.object not in ETYPE_NAME_MAP:
+ self.session.vreg.schema.add_relation_def(rdef)
@@ -882,7 +885,7 @@
if name in CORE_TYPES:
raise ValidationError(self.entity.eid, {None: self._cw._('can\'t be deleted')})
# delete every entities of this type
- if not name in ETYPE_NAME_MAP:
+ if name not in ETYPE_NAME_MAP:
self._cw.execute('DELETE %s X' % name)
MemSchemaCWETypeDel(self._cw, etype=name)
DropTable(self._cw, table=SQL_PREFIX + name)
@@ -1066,6 +1069,8 @@
return
subjtype = entity.stype.name
objtype = entity.otype.name
+ if subjtype in ETYPE_NAME_MAP or objtype in ETYPE_NAME_MAP:
+ return
rschema = self._cw.vreg.schema[entity.rtype.name]
# note: do not access schema rdef here, it may be added later by an
# operation
--- a/hooks/syncsources.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/syncsources.py Wed Jul 20 18:21:47 2011 +0200
@@ -132,7 +132,7 @@
if not session.deleted_in_transaction(schemacfg.eid):
source.add_schema_config(schemacfg, checkonly=checkonly)
elif session.deleted_in_transaction(schemacfg.eid):
- source.delete_schema_config(schemacfg, checkonly=checkonly)
+ source.del_schema_config(schemacfg, checkonly=checkonly)
else:
source.update_schema_config(schemacfg, checkonly=checkonly)
@@ -160,4 +160,4 @@
def __call__(self):
SourceMappingChangedOp.get_instance(self._cw).add_data(
(self._cw.entity_from_eid(self.eidfrom),
- self._cw.entity_from_eid(self.eidto)) )
+ self._cw.entity_from_eid(self.eidto).repo_source) )
--- a/hooks/test/unittest_hooks.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/test/unittest_hooks.py Wed Jul 20 18:21:47 2011 +0200
@@ -42,25 +42,31 @@
def test_html_tidy_hook(self):
req = self.request()
- entity = req.create_entity('Workflow', name=u'wf1', description_format=u'text/html',
- description=u'yo')
+ entity = req.create_entity('Workflow', name=u'wf1',
+ description_format=u'text/html',
+ description=u'yo')
self.assertEqual(entity.description, u'yo')
- entity = req.create_entity('Workflow', name=u'wf2', description_format=u'text/html',
- description=u'<b>yo')
+ entity = req.create_entity('Workflow', name=u'wf2',
+ description_format=u'text/html',
+ description=u'<b>yo')
self.assertEqual(entity.description, u'<b>yo</b>')
- entity = req.create_entity('Workflow', name=u'wf3', description_format=u'text/html',
- description=u'<b>yo</b>')
+ entity = req.create_entity('Workflow', name=u'wf3',
+ description_format=u'text/html',
+ description=u'<b>yo</b>')
self.assertEqual(entity.description, u'<b>yo</b>')
- entity = req.create_entity('Workflow', name=u'wf4', description_format=u'text/html',
- description=u'<b>R&D</b>')
+ entity = req.create_entity('Workflow', name=u'wf4',
+ description_format=u'text/html',
+ description=u'<b>R&D</b>')
self.assertEqual(entity.description, u'<b>R&D</b>')
- entity = req.create_entity('Workflow', name=u'wf5', description_format=u'text/html',
- description=u"<div>c'est <b>l'été")
+ entity = req.create_entity('Workflow', name=u'wf5',
+ description_format=u'text/html',
+ description=u"<div>c'est <b>l'été")
self.assertEqual(entity.description, u"<div>c'est <b>l'été</b></div>")
def test_nonregr_html_tidy_hook_no_update(self):
- entity = self.request().create_entity('Workflow', name=u'wf1', description_format=u'text/html',
- description=u'yo')
+ entity = self.request().create_entity('Workflow', name=u'wf1',
+ description_format=u'text/html',
+ description=u'yo')
entity.set_attributes(name=u'wf2')
self.assertEqual(entity.description, u'yo')
entity.set_attributes(description=u'R&D<p>yo')
@@ -90,7 +96,8 @@
self.assertEqual(entity.owned_by[0].eid, self.session.user.eid)
def test_user_login_stripped(self):
- u = self.create_user(' joe ')
+ req = self.request()
+ u = self.create_user(req, ' joe ')
tname = self.execute('Any L WHERE E login L, E eid %(e)s',
{'e': u.eid})[0][0]
self.assertEqual(tname, 'joe')
@@ -104,7 +111,8 @@
class UserGroupHooksTC(CubicWebTC):
def test_user_synchronization(self):
- self.create_user('toto', password='hop', commit=False)
+ req = self.request()
+ self.create_user(req, 'toto', password='hop', commit=False)
self.assertRaises(AuthenticationError,
self.repo.connect, u'toto', password='hop')
self.commit()
@@ -129,7 +137,8 @@
self.assertEqual(user.groups, set(('managers',)))
def test_user_composite_owner(self):
- ueid = self.create_user('toto').eid
+ req = self.request()
+ ueid = self.create_user(req, 'toto').eid
# composite of euser should be owned by the euser regardless of who created it
self.execute('INSERT EmailAddress X: X address "toto@logilab.fr", U use_email X '
'WHERE U login "toto"')
--- a/hooks/test/unittest_syncschema.py Mon May 16 16:24:00 2011 +0200
+++ b/hooks/test/unittest_syncschema.py Wed Jul 20 18:21:47 2011 +0200
@@ -251,7 +251,8 @@
'RT name "surname", E name "CWUser"')
self.commit()
# should not be able anymore to add cwuser without surname
- self.assertRaises(ValidationError, self.create_user, "toto")
+ req = self.request()
+ self.assertRaises(ValidationError, self.create_user, req, "toto")
self.rollback()
self.execute('SET DEF cardinality "?1" '
'WHERE DEF relation_type RT, DEF from_entity E,'
--- a/i18n/de.po Mon May 16 16:24:00 2011 +0200
+++ b/i18n/de.po Wed Jul 20 18:21:47 2011 +0200
@@ -162,7 +162,7 @@
msgstr ""
#, python-format
-msgid "'%s' action require 'linkattr' option"
+msgid "'%s' action requires 'linkattr' option"
msgstr ""
msgid "(UNEXISTANT EID)"
@@ -737,6 +737,18 @@
msgid "Submit bug report by mail"
msgstr "Diesen Bericht als E-Mail senden"
+msgid "TZDatetime"
+msgstr ""
+
+msgid "TZDatetime_plural"
+msgstr ""
+
+msgid "TZTime"
+msgstr ""
+
+msgid "TZTime_plural"
+msgstr ""
+
#, python-format
msgid "The view %s can not be applied to this query"
msgstr "Die Ansicht %s ist auf diese Anfrage nicht anwendbar."
@@ -1049,10 +1061,6 @@
msgid "add WorkflowTransition transition_of Workflow object"
msgstr "Workflow-Übergang"
-#, python-format
-msgid "add a %s"
-msgstr ""
-
msgctxt "inlined:CWRelation.from_entity.subject"
msgid "add a CWEType"
msgstr "einen Entitätstyp hinzufügen"
@@ -1192,6 +1200,9 @@
msgid "anonymous"
msgstr "anonym"
+msgid "anyrsetview"
+msgstr ""
+
msgid "april"
msgstr "April"
@@ -2323,6 +2334,9 @@
msgid "entity update"
msgstr "Aktualisierung der Entität"
+msgid "entityview"
+msgstr ""
+
msgid "error"
msgstr ""
@@ -2743,6 +2757,9 @@
"zeigt an, welcher Zustand standardmäßig benutzt werden soll, wenn eine "
"Entität erstellt wird"
+msgid "indifferent"
+msgstr "gleichgültig"
+
msgid "info"
msgstr "Information"
@@ -3453,7 +3470,7 @@
#, python-format
msgid ""
-"relation %s is supported but none if its definitions matches supported "
+"relation %s is supported but none of its definitions matches supported "
"entities"
msgstr ""
@@ -3748,6 +3765,9 @@
msgid "startup views"
msgstr "Start-Ansichten"
+msgid "startupview"
+msgstr ""
+
msgid "state"
msgstr "Zustand"
@@ -4014,7 +4034,7 @@
msgid "tr_count"
msgstr ""
-msgid "transaction undoed"
+msgid "transaction undone"
msgstr "Transaktion rückgängig gemacht"
#, python-format
--- a/i18n/en.po Mon May 16 16:24:00 2011 +0200
+++ b/i18n/en.po Wed Jul 20 18:21:47 2011 +0200
@@ -154,7 +154,7 @@
msgstr ""
#, python-format
-msgid "'%s' action require 'linkattr' option"
+msgid "'%s' action requires 'linkattr' option"
msgstr ""
msgid "(UNEXISTANT EID)"
@@ -711,6 +711,18 @@
msgid "Submit bug report by mail"
msgstr ""
+msgid "TZDatetime"
+msgstr "International date and time"
+
+msgid "TZDatetime_plural"
+msgstr "International dates and times"
+
+msgid "TZTime"
+msgstr "International time"
+
+msgid "TZTime_plural"
+msgstr "International times"
+
#, python-format
msgid "The view %s can not be applied to this query"
msgstr ""
@@ -1009,10 +1021,6 @@
msgid "add WorkflowTransition transition_of Workflow object"
msgstr "workflow-transition"
-#, python-format
-msgid "add a %s"
-msgstr ""
-
msgctxt "inlined:CWRelation.from_entity.subject"
msgid "add a CWEType"
msgstr "add an entity type"
@@ -1149,6 +1157,9 @@
msgid "anonymous"
msgstr ""
+msgid "anyrsetview"
+msgstr "rset views"
+
msgid "april"
msgstr ""
@@ -2267,6 +2278,9 @@
msgid "entity update"
msgstr ""
+msgid "entityview"
+msgstr "entity views"
+
msgid "error"
msgstr ""
@@ -2668,6 +2682,9 @@
"is created"
msgstr ""
+msgid "indifferent"
+msgstr "indifferent"
+
msgid "info"
msgstr ""
@@ -3305,7 +3322,7 @@
msgstr ""
msgid "read_permission"
-msgstr "can be read by"
+msgstr "read permission"
msgctxt "CWAttribute"
msgid "read_permission"
@@ -3324,11 +3341,11 @@
msgctxt "CWGroup"
msgid "read_permission_object"
-msgstr "can be read by"
+msgstr "has permission to read"
msgctxt "RQLExpression"
msgid "read_permission_object"
-msgstr "can be read by"
+msgstr "has permission to read"
msgid "regexp matching host(s) to which this config applies"
msgstr ""
@@ -3363,7 +3380,7 @@
#, python-format
msgid ""
-"relation %s is supported but none if its definitions matches supported "
+"relation %s is supported but none of its definitions matches supported "
"entities"
msgstr ""
@@ -3648,6 +3665,9 @@
msgid "startup views"
msgstr ""
+msgid "startupview"
+msgstr "startup views"
+
msgid "state"
msgstr ""
@@ -3909,7 +3929,7 @@
msgid "tr_count"
msgstr "transition number"
-msgid "transaction undoed"
+msgid "transaction undone"
msgstr ""
#, python-format
--- a/i18n/es.po Mon May 16 16:24:00 2011 +0200
+++ b/i18n/es.po Wed Jul 20 18:21:47 2011 +0200
@@ -4,9 +4,9 @@
msgid ""
msgstr ""
"Project-Id-Version: cubicweb 2.46.0\n"
-"PO-Revision-Date: 2010-11-27 07:59+0100\n"
-"Last-Translator: Celso Flores<celso.flores@crealibre.com>, Carlos Balderas "
-"<carlos.balderas@crealibre.com>\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2011-05-03 12:53-0600\n"
+"Last-Translator: CreaLibre <info@crealibre.com>\n"
"Language-Team: es <contact@logilab.fr>\n"
"Language: \n"
"MIME-Version: 1.0\n"
@@ -43,9 +43,12 @@
#, python-format
msgid "\"action\" must be specified in options; allowed values are %s"
msgstr ""
+"\"action\" debe estar especificada en opciones; los valores permitidos son : "
+"%s"
msgid "\"role=subject\" or \"role=object\" must be specified in options"
msgstr ""
+"\"role=subject\" o \"role=object\" debe ser especificado en las opciones"
#, python-format
msgid "%(attr)s set to %(newvalue)s"
@@ -61,7 +64,7 @@
#, python-format
msgid "%(etype)s by %(author)s"
-msgstr ""
+msgstr "%(etype)s por %(author)s"
#, python-format
msgid "%(firstname)s %(surname)s"
@@ -133,7 +136,7 @@
#, python-format
msgid "%s could be supported"
-msgstr ""
+msgstr "%s podría ser mantenido"
#, python-format
msgid "%s error report"
@@ -145,7 +148,7 @@
#, python-format
msgid "%s relation should not be in mapped"
-msgstr ""
+msgstr "la relación %s no debería estar mapeada"
#, python-format
msgid "%s software version of the database"
@@ -157,11 +160,11 @@
#, python-format
msgid "'%s' action doesn't take any options"
-msgstr ""
+msgstr "la acción '%s' no acepta opciones"
#, python-format
-msgid "'%s' action require 'linkattr' option"
-msgstr ""
+msgid "'%s' action requires 'linkattr' option"
+msgstr "la acción '%s' requiere una opción 'linkattr'"
msgid "(UNEXISTANT EID)"
msgstr "(EID INEXISTENTE"
@@ -219,7 +222,7 @@
"div>"
msgid "<not specified>"
-msgstr ""
+msgstr "<no especificado>"
msgid "?*"
msgstr "0..1 0..n"
@@ -357,28 +360,28 @@
msgstr "Relaciones"
msgid "CWSource"
-msgstr ""
+msgstr "Fuente de datos"
msgid "CWSourceHostConfig"
-msgstr ""
+msgstr "Configuración de Fuente"
msgid "CWSourceHostConfig_plural"
-msgstr ""
+msgstr "Configuraciones de fuente"
msgid "CWSourceSchemaConfig"
-msgstr ""
+msgstr "Configuraciones de Esquema de Fuente"
msgid "CWSourceSchemaConfig_plural"
-msgstr ""
+msgstr "Configuraciones de Esquema de Fuente"
msgid "CWSource_plural"
-msgstr ""
+msgstr "Fuentes de Datos"
msgid "CWUniqueTogetherConstraint"
-msgstr ""
+msgstr "Restricción de Singularidad"
msgid "CWUniqueTogetherConstraint_plural"
-msgstr ""
+msgstr "Restricciones de Singularidad"
msgid "CWUser"
msgstr "Usuario"
@@ -436,7 +439,7 @@
#, python-format
msgid "Data connection graph for %s"
-msgstr ""
+msgstr "Gráfica de conexión de datos para %s"
msgid "Date"
msgstr "Fecha"
@@ -457,7 +460,7 @@
msgstr "Decimales"
msgid "Detected problems"
-msgstr ""
+msgstr "Problemas detectados"
msgid "Do you want to delete the following element(s) ?"
msgstr "Desea eliminar el(los) elemento(s) siguiente(s)"
@@ -479,7 +482,7 @@
msgstr "Entidades"
msgid "Entity and relation supported by this source"
-msgstr ""
+msgstr "Entidades y relaciones aceptadas por esta fuente"
msgid "ExternalUri"
msgstr "Uri externo"
@@ -506,7 +509,7 @@
msgstr "Recolector de basura en memoria"
msgid "Got rhythm?"
-msgstr ""
+msgstr "Tenemos Ritmo?"
msgid "Help"
msgstr "Ayuda"
@@ -533,7 +536,7 @@
msgstr "Clases buscadas"
msgid "Manage"
-msgstr ""
+msgstr "Administración"
msgid "Most referenced classes"
msgstr "Clases más referenciadas"
@@ -575,16 +578,16 @@
msgstr "Nueva definición de relación final"
msgid "New CWSource"
-msgstr ""
+msgstr "Nueva fuente"
msgid "New CWSourceHostConfig"
-msgstr ""
+msgstr "Nueva configuración de fuente"
msgid "New CWSourceSchemaConfig"
-msgstr ""
+msgstr "Nueva parte de mapeo de fuente"
msgid "New CWUniqueTogetherConstraint"
-msgstr ""
+msgstr "Nueva restricción de singularidad"
msgid "New CWUser"
msgstr "Agregar usuario"
@@ -685,7 +688,7 @@
msgstr "Buscar"
msgid "Site information"
-msgstr ""
+msgstr "Información del Sitio"
msgid "SizeConstraint"
msgstr "Restricción de tamaño"
@@ -695,6 +698,9 @@
"authorized keys depending on the source's type, overriding values defined on "
"the source."
msgstr ""
+"Configuración de la fuente por un \"host\" específico. Una clave=valor por "
+"línea, las claves permitidas dependen del tipo de fuente. Estos valores son "
+"prioritarios a los valores definidos en la fuente."
msgid "Startup views"
msgstr "Vistas de inicio"
@@ -732,6 +738,18 @@
msgid "Submit bug report by mail"
msgstr "Enviar este reporte por email"
+msgid "TZDatetime"
+msgstr "Fecha y hora internacional"
+
+msgid "TZDatetime_plural"
+msgstr "Fechas y horas internacionales"
+
+msgid "TZTime"
+msgstr "Hora internacional"
+
+msgid "TZTime_plural"
+msgstr "Horas internacionales"
+
#, python-format
msgid "The view %s can not be applied to this query"
msgstr "La vista %s no puede ser aplicada a esta búsqueda"
@@ -780,16 +798,16 @@
msgstr "Esta definición de relación no final"
msgid "This CWSource"
-msgstr ""
+msgstr "Esta fuente"
msgid "This CWSourceHostConfig"
-msgstr ""
+msgstr "Esta configuración de fuente"
msgid "This CWSourceSchemaConfig"
-msgstr ""
+msgstr "Esta parte de mapeo de fuente"
msgid "This CWUniqueTogetherConstraint"
-msgstr ""
+msgstr "Esta restricción de singularidad"
msgid "This CWUser"
msgstr "Este usuario"
@@ -844,6 +862,8 @@
msgid "URLs from which content will be imported. You can put one url per line"
msgstr ""
+"URLs desde el cual el contenido sera importado. Usted puede incluir un URL "
+"por línea."
msgid "UniqueConstraint"
msgstr "Restricción de Unicidad"
@@ -1007,10 +1027,10 @@
msgstr "Definición de relación"
msgid "add CWSourceHostConfig cw_host_config_of CWSource object"
-msgstr ""
+msgstr "configuración del host"
msgid "add CWUniqueTogetherConstraint constraint_of CWEType object"
-msgstr ""
+msgstr "restricción de singularidad"
msgid "add CWUser in_group CWGroup object"
msgstr "Usuario"
@@ -1051,10 +1071,6 @@
msgid "add WorkflowTransition transition_of Workflow object"
msgstr "Transición Workflow"
-#, python-format
-msgid "add a %s"
-msgstr ""
-
msgctxt "inlined:CWRelation.from_entity.subject"
msgid "add a CWEType"
msgstr "Agregar un tipo de entidad"
@@ -1068,10 +1084,10 @@
msgstr "Agregar un tipo de relación"
msgid "add a CWSource"
-msgstr ""
+msgstr "agregar una fuente"
msgid "add a CWSourceSchemaConfig"
-msgstr ""
+msgstr "agregar una parte de mapeo"
msgctxt "inlined:CWUser.use_email.subject"
msgid "add a EmailAddress"
@@ -1142,7 +1158,7 @@
msgstr "permite definir un Workflow específico para una entidad"
msgid "allowed options depends on the source type"
-msgstr ""
+msgstr "las opciones permitidas dependen del tipo de fuente"
msgid "allowed transitions from this state"
msgstr "transiciones autorizadas desde este estado"
@@ -1193,16 +1209,19 @@
msgid "anonymous"
msgstr "anónimo"
+msgid "anyrsetview"
+msgstr ""
+
msgid "april"
msgstr "Abril"
#, python-format
msgid "archive for %(author)s"
-msgstr ""
+msgstr "archivo de %(author)s"
#, python-format
msgid "archive for %(month)s/%(year)s"
-msgstr ""
+msgstr "archivo del %(month)s/%(year)s"
#, python-format
msgid "at least one relation %(rtype)s is required on %(etype)s (%(eid)s)"
@@ -1215,6 +1234,8 @@
msgid "attribute/relation can't be mapped, only entity and relation types"
msgstr ""
+"los atributos y las relaciones no pueden ser mapeados, solamente los tipos "
+"de entidad y de relación"
msgid "august"
msgstr "Agosto"
@@ -1326,7 +1347,7 @@
msgstr "no puede modificar el atributo %s"
msgid "can't change this relation"
-msgstr ""
+msgstr "no puede modificar esta relación"
#, python-format
msgid "can't connect to source %s, some data may be missing"
@@ -1340,10 +1361,10 @@
msgstr "no puede tener varias salidas en el mismo estado"
msgid "can't mix dontcross and maycross options"
-msgstr ""
+msgstr "no puede mezclar las opciones dontcross y maycross"
msgid "can't mix dontcross and write options"
-msgstr ""
+msgstr "no puede mezclar las opciones dontcross y write"
#, python-format
msgid "can't parse %(value)r (expected %(format)s)"
@@ -1358,7 +1379,7 @@
"cardinalidad %(card)s"
msgid "cancel"
-msgstr ""
+msgstr "anular"
msgid "cancel select"
msgstr "Cancelar la selección"
@@ -1391,7 +1412,7 @@
msgstr "Ver la entidad creada"
msgid "click here to see edited entity"
-msgstr ""
+msgstr "seleccione aquí para ver la entidad modificada"
msgid "click on the box to cancel the deletion"
msgstr "Seleccione la zona de edición para cancelar la eliminación"
@@ -1475,15 +1496,15 @@
msgstr "condiciones"
msgid "config"
-msgstr ""
+msgstr "configuración"
msgctxt "CWSource"
msgid "config"
-msgstr ""
+msgstr "configuración"
msgctxt "CWSourceHostConfig"
msgid "config"
-msgstr ""
+msgstr "configuración"
msgid "config mode"
msgstr "Modo de configuración"
@@ -1516,18 +1537,18 @@
msgstr "Fábrica de restricciones"
msgid "constraint_of"
-msgstr ""
+msgstr "restricción de"
msgctxt "CWUniqueTogetherConstraint"
msgid "constraint_of"
-msgstr ""
+msgstr "restricción de"
msgid "constraint_of_object"
-msgstr ""
+msgstr "restringida por"
msgctxt "CWEType"
msgid "constraint_of_object"
-msgstr ""
+msgstr "restringida por"
msgid "constraints"
msgstr "Restricciones"
@@ -1622,12 +1643,12 @@
msgid ""
"creating CWSourceHostConfig (CWSourceHostConfig cw_host_config_of CWSource "
"%(linkto)s)"
-msgstr ""
+msgstr "creación de una configuración host para la fuente %(linkto)s"
msgid ""
"creating CWUniqueTogetherConstraint (CWUniqueTogetherConstraint "
"constraint_of CWEType %(linkto)s)"
-msgstr ""
+msgstr "creación de una restricción de singularidad en %(linkto)s"
msgid "creating CWUser (CWUser in_group CWGroup %(linkto)s)"
msgstr "Creación de un usuario para agregar al grupo %(linkto)s"
@@ -1807,7 +1828,7 @@
msgstr "Metadatos de la Entidad"
msgid "ctxcomponents_metadata_description"
-msgstr ""
+msgstr "espacio que incluye los metadatos de la entidad actual"
msgid "ctxcomponents_possible_views_box"
msgstr "Caja de Vistas Posibles"
@@ -1844,10 +1865,10 @@
msgstr "Muestra las vistas de inicio de la aplicación"
msgid "ctxcomponents_userstatus"
-msgstr ""
+msgstr "estado del usuario"
msgid "ctxcomponents_userstatus_description"
-msgstr ""
+msgstr "establece el estado del usuario"
msgid "ctxcomponents_wfhistory"
msgstr "Histórico del workflow."
@@ -1867,64 +1888,64 @@
msgstr "Workflow de"
msgid "cw_for_source"
-msgstr ""
+msgstr "fuente"
msgctxt "CWSourceSchemaConfig"
msgid "cw_for_source"
-msgstr ""
+msgstr "fuente"
msgid "cw_for_source_object"
-msgstr ""
+msgstr "elemento de mapeo"
msgctxt "CWSource"
msgid "cw_for_source_object"
-msgstr ""
+msgstr "elemento de mapeo"
msgid "cw_host_config_of"
-msgstr ""
+msgstr "configuración del host de"
msgctxt "CWSourceHostConfig"
msgid "cw_host_config_of"
-msgstr ""
+msgstr "configuración del host de"
msgid "cw_host_config_of_object"
-msgstr ""
+msgstr "tiene la configuración del host"
msgctxt "CWSource"
msgid "cw_host_config_of_object"
-msgstr ""
+msgstr "tiene la configuración del host"
msgid "cw_schema"
-msgstr ""
+msgstr "esquema"
msgctxt "CWSourceSchemaConfig"
msgid "cw_schema"
-msgstr ""
+msgstr "esquema"
msgid "cw_schema_object"
-msgstr ""
+msgstr "mapeado por"
msgctxt "CWAttribute"
msgid "cw_schema_object"
-msgstr ""
+msgstr "mapeado por"
msgctxt "CWEType"
msgid "cw_schema_object"
-msgstr ""
+msgstr "mapeado por"
msgctxt "CWRType"
msgid "cw_schema_object"
-msgstr ""
+msgstr "mapeado por"
msgctxt "CWRelation"
msgid "cw_schema_object"
-msgstr ""
+msgstr "mapeado por"
msgid "cw_source"
-msgstr ""
+msgstr "desde la fuente de datos"
msgid "cw_source_object"
-msgstr ""
+msgstr "entidades"
msgid "cwetype-box"
msgstr "Vista \"caja\""
@@ -1954,10 +1975,10 @@
msgstr "Permisos"
msgid "cwsource-main"
-msgstr ""
+msgstr "descripción"
msgid "cwsource-mapping"
-msgstr ""
+msgstr "mapeo"
msgid "cwuri"
msgstr "Uri Interna"
@@ -1966,16 +1987,16 @@
msgstr "Url del repertorio de datos"
msgid "data sources"
-msgstr ""
+msgstr "fuente de datos"
msgid "data sources management"
-msgstr ""
+msgstr "administración de fuentes de datos"
msgid "date"
msgstr "Fecha"
msgid "day"
-msgstr ""
+msgstr "día"
msgid "deactivate"
msgstr "Desactivar"
@@ -2066,7 +2087,7 @@
msgstr "Define como salir de un sub-Workflow"
msgid "defines a sql-level multicolumn unique index"
-msgstr ""
+msgstr "define un índice SQL único a través de varias columnas"
msgid ""
"defines what's the property is applied for. You must select this first to be "
@@ -2312,6 +2333,8 @@
msgid "entity and relation types can't be mapped, only attributes or relations"
msgstr ""
+"los tipos de entidad y relación no pueden ser mapeados, solo los atributos y "
+"las relaciones"
msgid "entity copied"
msgstr "Entidad copiada"
@@ -2353,8 +2376,11 @@
msgid "entity update"
msgstr "Actualización de la Entidad"
+msgid "entityview"
+msgstr ""
+
msgid "error"
-msgstr ""
+msgstr "error"
msgid "error while embedding page"
msgstr "Error durante la inclusión de la página"
@@ -2418,10 +2444,10 @@
msgstr "Faceta creada por"
msgid "facets_cw_source-facet"
-msgstr ""
+msgstr "faceta \"fuente de datos\""
msgid "facets_cw_source-facet_description"
-msgstr ""
+msgstr "fuente de datos"
msgid "facets_cwfinal-facet"
msgstr "Faceta \"final\""
@@ -2574,7 +2600,7 @@
msgstr "Texto indexado"
msgid "gc"
-msgstr ""
+msgstr "fuga de memoria"
msgid "generic plot"
msgstr "Gráfica Genérica"
@@ -2637,10 +2663,10 @@
msgstr "Contiene el texto"
msgid "header-left"
-msgstr ""
+msgstr "encabezado (izquierdo)"
msgid "header-right"
-msgstr ""
+msgstr "encabezado (derecho)"
msgid "hide filter form"
msgstr "Esconder el filtro"
@@ -2773,6 +2799,9 @@
msgstr ""
"Indica cual estado deberá ser utilizado por defecto al crear una entidad"
+msgid "indifferent"
+msgstr "indifferente"
+
msgid "info"
msgstr "Información del Sistema"
@@ -2807,6 +2836,8 @@
#, python-format
msgid "inlined relation %(rtype)s of %(etype)s should be supported"
msgstr ""
+"la relación %(rtype)s del tipo de entidad %(etype)s debe ser aceptada "
+"('inlined')"
msgid "instance home"
msgstr "Repertorio de la Instancia"
@@ -2912,17 +2943,17 @@
msgstr "Fecha de la última modificación de una entidad "
msgid "latest synchronization time"
-msgstr ""
+msgstr "fecha de la última sincronización"
msgid "latest update on"
msgstr "Actualizado el"
msgid "latest_retrieval"
-msgstr ""
+msgstr "última sincronización"
msgctxt "CWSource"
msgid "latest_retrieval"
-msgstr ""
+msgstr "fecha de la última sincronización de la fuente"
msgid "left"
msgstr "izquierda"
@@ -2980,7 +3011,7 @@
msgstr "Usuario"
msgid "login / password"
-msgstr ""
+msgstr "usuario / contraseña"
msgid "login or email"
msgstr "Usuario o dirección de correo"
@@ -2999,7 +3030,7 @@
msgstr "Informaciones Generales"
msgid "main_tab"
-msgstr ""
+msgstr "descripción"
msgid "mainvars"
msgstr "Variables principales"
@@ -3030,11 +3061,11 @@
msgstr "Marzo"
msgid "match_host"
-msgstr ""
+msgstr "para el host"
msgctxt "CWSourceHostConfig"
msgid "match_host"
-msgstr ""
+msgstr "para el host"
msgid "maximum number of characters in short description"
msgstr "Máximo de caracteres en las descripciones cortas"
@@ -3062,7 +3093,7 @@
msgstr "Parámetros faltantes a la entidad %s"
msgid "modification"
-msgstr ""
+msgstr "modificación"
msgid "modification_date"
msgstr "Fecha de modificación"
@@ -3074,7 +3105,7 @@
msgstr "Lunes"
msgid "month"
-msgstr ""
+msgstr "mes"
msgid "more actions"
msgstr "Más acciones"
@@ -3121,7 +3152,7 @@
msgctxt "CWSource"
msgid "name"
-msgstr ""
+msgstr "nombre"
msgctxt "State"
msgid "name"
@@ -3150,7 +3181,7 @@
"selección de ser necesario (separarlas con comas)"
msgid "name of the source"
-msgstr ""
+msgstr "nombre de la fuente"
msgid "name or identifier of the permission"
msgstr "Nombre o identificador del permiso"
@@ -3267,7 +3298,7 @@
msgctxt "CWSourceSchemaConfig"
msgid "options"
-msgstr ""
+msgstr "opciones"
msgid "order"
msgstr "Orden"
@@ -3308,14 +3339,16 @@
msgstr "Página no encontrada."
msgid "parser"
-msgstr ""
+msgstr "analizador (parser)"
msgctxt "CWSource"
msgid "parser"
-msgstr ""
+msgstr "analizador (parser)"
msgid "parser to use to extract entities from content retrieved at given URLs."
msgstr ""
+"analizador (parser) que sirve para extraer entidades y relaciones del "
+"contenido recuperado de las URLs."
msgid "password"
msgstr "Contraseña"
@@ -3396,7 +3429,7 @@
msgstr "Dirección principal de correo electrónico de"
msgid "profile"
-msgstr ""
+msgstr "perfil"
msgid "progress"
msgstr "Progreso"
@@ -3414,7 +3447,7 @@
msgstr "Permisos"
msgid "rdf"
-msgstr ""
+msgstr "rdf"
msgid "read"
msgstr "Lectura"
@@ -3447,6 +3480,8 @@
msgid "regexp matching host(s) to which this config applies"
msgstr ""
+"expresión regular de los nombres de hosts a los cuales esta configuración "
+"aplica"
msgid "registry"
msgstr "Registro"
@@ -3469,18 +3504,24 @@
"relation %(rtype)s with %(etype)s as %(role)s is supported but no target "
"type supported"
msgstr ""
+"la relación %(rtype)s con %(etype)s como %(role)s es aceptada pero ningún "
+"tipo target es aceptado"
#, python-format
msgid ""
"relation %(type)s with %(etype)s as %(role)s and target type %(target)s is "
"mandatory but not supported"
msgstr ""
+"la relación %(type)s con %(etype)s como %(role)s y tipo objetivo %(target)s "
+"es obligatoria pero no mantenida"
#, python-format
msgid ""
-"relation %s is supported but none if its definitions matches supported "
+"relation %s is supported but none of its definitions matches supported "
"entities"
msgstr ""
+"la relación %s es aceptada pero ninguna de sus definiciones corresponden a "
+"los tipos de entidades aceptadas"
msgid "relation add"
msgstr "Agregar Relación"
@@ -3507,21 +3548,21 @@
msgstr "Definición de Relaciones"
msgid "relations"
-msgstr ""
+msgstr "relaciones"
msgctxt "CWUniqueTogetherConstraint"
msgid "relations"
-msgstr ""
+msgstr "relaciones"
msgid "relations deleted"
msgstr "Relaciones Eliminadas"
msgid "relations_object"
-msgstr ""
+msgstr "relaciones de"
msgctxt "CWRType"
msgid "relations_object"
-msgstr ""
+msgstr "relaciones de"
msgid "relative url of the bookmarked page"
msgstr "Url relativa de la página"
@@ -3633,7 +3674,7 @@
msgstr "Seguridad"
msgid "see more"
-msgstr ""
+msgstr "ver más"
msgid "see them all"
msgstr "Ver todos"
@@ -3728,7 +3769,7 @@
msgstr "Una propiedad específica al Sistema no puede ser propia al usuario"
msgid "siteinfo"
-msgstr ""
+msgstr "información"
msgid "some errors occurred:"
msgstr "Algunos errores encontrados :"
@@ -3744,6 +3785,8 @@
"source's configuration. One key=value per line, authorized keys depending on "
"the source's type"
msgstr ""
+"configuración de fuentes. Una clave=valor por línea, las claves permitidas "
+"dependen del tipo de la fuente."
msgid "sparql xml"
msgstr "XML Sparql"
@@ -3767,11 +3810,14 @@
#, python-format
msgid "specifying %s is mandatory"
-msgstr ""
+msgstr "especificar %s es obligatorio"
msgid "startup views"
msgstr "Vistas de inicio"
+msgid "startupview"
+msgstr ""
+
msgid "state"
msgstr "Estado"
@@ -3899,7 +3945,7 @@
msgstr "Simétrico"
msgid "synchronization-interval must be greater than 1 minute"
-msgstr ""
+msgstr "synchronization-interval debe ser mayor a 1 minuto"
msgid "table"
msgstr "Tabla"
@@ -3933,6 +3979,7 @@
msgid "the system source has its configuration stored on the file-system"
msgstr ""
+"el sistema fuente tiene su configuración almacenada en el sistema de archivos"
#, python-format
msgid "the value \"%s\" is already used, use another one"
@@ -3945,13 +3992,13 @@
msgstr "Esta Entidad es propiedad de"
msgid "this parser doesn't use a mapping"
-msgstr ""
+msgstr "este analizador (parser) no utiliza mapeo"
msgid "this resource does not exist"
msgstr "Este recurso no existe"
msgid "this source doesn't use a mapping"
-msgstr ""
+msgstr "esta fuente no utiliza mapeo"
msgid "thursday"
msgstr "Jueves"
@@ -4022,7 +4069,7 @@
msgstr "Transición hacia este Estado"
msgid "today"
-msgstr ""
+msgstr "hoy"
msgid "todo_by"
msgstr "Asignada a"
@@ -4031,13 +4078,13 @@
msgstr "Cambiar valor"
msgid "tr_count"
-msgstr ""
+msgstr "n° de transición"
msgctxt "TrInfo"
msgid "tr_count"
-msgstr ""
-
-msgid "transaction undoed"
+msgstr "n° de transición"
+
+msgid "transaction undone"
msgstr "Transacciones Anuladas"
#, python-format
@@ -4090,7 +4137,7 @@
msgctxt "CWSource"
msgid "type"
-msgstr ""
+msgstr "tipo"
msgctxt "Transition"
msgid "type"
@@ -4104,7 +4151,7 @@
msgstr "Escriba aquí su consulta en Sparql"
msgid "type of the source"
-msgstr ""
+msgstr "tipo de la fuente"
msgid "ui"
msgstr "Interfaz Genérica"
@@ -4159,18 +4206,18 @@
#, python-format
msgid "unknown option(s): %s"
-msgstr ""
+msgstr "opcion(es) desconocida(s): %s"
#, python-format
msgid "unknown options %s"
-msgstr ""
+msgstr "opciones desconocidas: %s"
#, python-format
msgid "unknown property key %s"
msgstr "Clave de Propiedad desconocida: %s"
msgid "unknown source type"
-msgstr ""
+msgstr "tipo de fuente desconocida"
msgid "unknown vocabulary:"
msgstr "Vocabulario desconocido: "
@@ -4225,11 +4272,11 @@
msgstr "URI"
msgid "url"
-msgstr ""
+msgstr "url"
msgctxt "CWSource"
msgid "url"
-msgstr ""
+msgstr "url"
msgid "use template languages"
msgstr "Utilizar plantillas de lenguaje"
@@ -4297,10 +4344,10 @@
msgstr "Usuarios"
msgid "users and groups"
-msgstr ""
+msgstr "usuarios y grupos"
msgid "users and groups management"
-msgstr ""
+msgstr "usuarios y grupos de administradores"
msgid "users using this bookmark"
msgstr "Usuarios utilizando este Favorito"
@@ -4377,13 +4424,13 @@
#, python-format
msgid "violates unique_together constraints (%s)"
-msgstr ""
+msgstr "viola el principio (o restricción) de singularidad (%s)"
msgid "visible"
msgstr "Visible"
msgid "warning"
-msgstr ""
+msgstr "atención"
msgid "we are not yet ready to handle this query"
msgstr "Aún no podemos manejar este tipo de consulta Sparql"
@@ -4482,77 +4529,16 @@
#, python-format
msgid "you may want to specify something for %s"
-msgstr ""
+msgstr "usted desea quizás especificar algo para la relación %s"
msgid "you should probably delete that property"
-msgstr "Debería probablamente suprimir esta propriedad"
+msgstr "probablamente debería suprimir esta propriedad"
#, python-format
msgid "you should un-inline relation %s which is supported and may be crossed "
msgstr ""
-
-#~ msgid "Attributes with non default permissions:"
-#~ msgstr "Atributos con permisos no estándares"
-
-#~ msgid "Entity types"
-#~ msgstr "Tipos de entidades"
-
-#~ msgid "Index"
-#~ msgstr "Índice"
-
-#~ msgid "Permissions for entity types"
-#~ msgstr "Permisos por tipos de entidad"
-
-#~ msgid "Permissions for relations"
-#~ msgstr "Permisos por las relaciones"
-
-#~ msgid "Relation types"
-#~ msgstr "Tipos de relación"
-
-#~ msgid "am/pm calendar (month)"
-#~ msgstr "calendario am/pm (mes)"
-
-#~ msgid "am/pm calendar (semester)"
-#~ msgstr "calendario am/pm (semestre)"
-
-#~ msgid "am/pm calendar (week)"
-#~ msgstr "calendario am/pm (semana)"
-
-#~ msgid "am/pm calendar (year)"
-#~ msgstr "calendario am/pm (año)"
-
-#~ msgid "application entities"
-#~ msgstr "Entidades de la aplicación"
-
-#~ msgid "calendar (month)"
-#~ msgstr "calendario (mensual)"
-
-#~ msgid "calendar (semester)"
-#~ msgstr "calendario (semestral)"
-
-#~ msgid "calendar (week)"
-#~ msgstr "calendario (semanal)"
-
-#~ msgid "calendar (year)"
-#~ msgstr "calendario (anual)"
-
-#~ msgid "create an index page"
-#~ msgstr "Crear una página de inicio"
-
-#~ msgid "edit the index page"
-#~ msgstr "Modificar la página de inicio"
-
-#~ msgid "schema entities"
-#~ msgstr "Entidades del esquema"
-
-#~ msgid "schema-security"
-#~ msgstr "Seguridad"
-
-#~ msgid "system entities"
-#~ msgstr "Entidades del sistema"
-
-#~ msgid "timestamp of the latest source synchronization."
-#~ msgstr "Fecha de la última sincronización de la fuente."
-
-#~ msgid "up"
-#~ msgstr "Arriba"
+"usted debe quitar la puesta en línea de la relación %s que es aceptada y "
+"puede ser cruzada"
+
+#~ msgid "add a %s"
+#~ msgstr "agregar un %s"
--- a/i18n/fr.po Mon May 16 16:24:00 2011 +0200
+++ b/i18n/fr.po Wed Jul 20 18:21:47 2011 +0200
@@ -4,7 +4,7 @@
msgid ""
msgstr ""
"Project-Id-Version: cubicweb 2.46.0\n"
-"PO-Revision-Date: 2011-01-03 14:35+0100\n"
+"PO-Revision-Date: 2011-06-23 10:23+0200\n"
"Last-Translator: Logilab Team <contact@logilab.fr>\n"
"Language-Team: fr <contact@logilab.fr>\n"
"Language: \n"
@@ -162,7 +162,7 @@
msgstr "l'action '%s' ne prend pas d'option"
#, python-format
-msgid "'%s' action require 'linkattr' option"
+msgid "'%s' action requires 'linkattr' option"
msgstr "l'action '%s' nécessite une option 'linkattr'"
msgid "(UNEXISTANT EID)"
@@ -449,7 +449,7 @@
msgstr "Date et heure"
msgid "Datetime_plural"
-msgstr "Date et heure"
+msgstr "Dates et heures"
msgid "Decimal"
msgstr "Nombre décimal"
@@ -736,6 +736,18 @@
msgid "Submit bug report by mail"
msgstr "Soumettre ce rapport par email"
+msgid "TZDatetime"
+msgstr "Date et heure internationale"
+
+msgid "TZDatetime_plural"
+msgstr "Dates et heures internationales"
+
+msgid "TZTime"
+msgstr "Heure internationale"
+
+msgid "TZTime_plural"
+msgstr "Heures internationales"
+
#, python-format
msgid "The view %s can not be applied to this query"
msgstr "La vue %s ne peut être appliquée à cette requête"
@@ -1057,10 +1069,6 @@
msgid "add WorkflowTransition transition_of Workflow object"
msgstr "transition workflow"
-#, python-format
-msgid "add a %s"
-msgstr "ajouter un %s"
-
msgctxt "inlined:CWRelation.from_entity.subject"
msgid "add a CWEType"
msgstr "ajouter un type d'entité sujet"
@@ -1199,6 +1207,9 @@
msgid "anonymous"
msgstr "anonyme"
+msgid "anyrsetview"
+msgstr "vues \"tous les rset\""
+
msgid "april"
msgstr "avril"
@@ -1934,10 +1945,10 @@
msgstr "mappé par"
msgid "cw_source"
-msgstr "from data source"
+msgstr "source"
msgid "cw_source_object"
-msgstr "entities"
+msgstr "entités"
msgid "cwetype-box"
msgstr "vue \"boîte\""
@@ -2363,6 +2374,9 @@
msgid "entity update"
msgstr "mise à jour d'entité"
+msgid "entityview"
+msgstr "vues d'entité"
+
msgid "error"
msgstr "erreur"
@@ -2783,6 +2797,9 @@
msgstr ""
"indique quel état devrait être utilisé par défaut lorsqu'une entité est créée"
+msgid "indifferent"
+msgstr "indifférent"
+
msgid "info"
msgstr "information"
@@ -3500,7 +3517,7 @@
#, python-format
msgid ""
-"relation %s is supported but none if its definitions matches supported "
+"relation %s is supported but none of its definitions matches supported "
"entities"
msgstr ""
"la relation %s est supportée mais aucune de ses définitions ne correspondent "
@@ -3799,6 +3816,9 @@
msgid "startup views"
msgstr "vues de départ"
+msgid "startupview"
+msgstr "vues de départ"
+
msgid "state"
msgstr "état"
@@ -4065,8 +4085,8 @@
msgid "tr_count"
msgstr "n° de transition"
-msgid "transaction undoed"
-msgstr "transaction annulées"
+msgid "transaction undone"
+msgstr "transaction annulée"
#, python-format
msgid "transition %(tr)s isn't allowed from %(st)s"
@@ -4535,6 +4555,9 @@
#~ msgid "Relation types"
#~ msgstr "Types de relation"
+#~ msgid "add a %s"
+#~ msgstr "ajouter un %s"
+
#~ msgid "am/pm calendar (month)"
#~ msgstr "calendrier am/pm (mois)"
--- a/migration.py Mon May 16 16:24:00 2011 +0200
+++ b/migration.py Wed Jul 20 18:21:47 2011 +0200
@@ -492,6 +492,8 @@
self.warnings.append(
'cube %s depends on %s but constraint badly '
'formatted: %s' % (cube, name, constraint))
+ else:
+ self.reverse_dependencies[name].add( (None, None, cube) )
# check consistency
for cube, versions in sorted(self.reverse_dependencies.items()):
oper, version, source = None, None, None
@@ -507,6 +509,8 @@
if version_strictly_lower(version, ver):
version = ver
source = src
+ elif op == None:
+ continue
else:
print 'unable to handle this case', oper, version, op, ver
# "solve" constraint satisfaction problem
@@ -517,5 +521,7 @@
if oper in ('>=','='):
if lower_strict:
self.errors.append( ('update', cube, version, source) )
+ elif oper is None:
+ pass # no constraint on version
else:
print 'unknown operator', oper
--- a/misc/migration/3.10.0_Any.py Mon May 16 16:24:00 2011 +0200
+++ b/misc/migration/3.10.0_Any.py Wed Jul 20 18:21:47 2011 +0200
@@ -32,7 +32,7 @@
# rename cwprops for boxes/contentnavigation
for x in rql('Any X,XK WHERE X pkey XK, '
- 'X pkey ~= "boxes.%s" OR '
- 'X pkey ~= "contentnavigation.%s"').entities():
+ 'X pkey ~= "boxes.%" OR '
+ 'X pkey ~= "contentnavigation.%"').entities():
x.set_attributes(pkey=u'ctxcomponents.' + x.pkey.split('.', 1)[1])
--- a/misc/migration/3.10.9_Any.py Mon May 16 16:24:00 2011 +0200
+++ b/misc/migration/3.10.9_Any.py Wed Jul 20 18:21:47 2011 +0200
@@ -11,7 +11,7 @@
from logilab.common.shellutils import progress
from cubicweb.server.session import hooks_control
rset = rql('Any X, XC WHERE X cwuri XC, X cwuri ~= "%/eid/%"')
- title = "%i entites to fix" % len(rset)
+ title = "%i entities to fix" % len(rset)
nbops = rset.rowcount
enabled = interactive_mode
with progress(title=title, nbops=nbops, size=30, enabled=enabled) as pb:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.12.0_Any.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,4 @@
+if schema['TZDatetime'].eid is None:
+ add_entity_type('TZDatetime')
+if schema['TZTime'].eid is None:
+ add_entity_type('TZTime')
--- a/misc/migration/bootstrapmigration_repository.py Mon May 16 16:24:00 2011 +0200
+++ b/misc/migration/bootstrapmigration_repository.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -94,7 +94,7 @@
drop_relation_definition('CWRType', '%s_permission' % action, 'RQLExpression')
sync_schema_props_perms('read_permission', syncperms=False) # fix read_permission cardinality
-if applcubicwebversion < (3, 9, 6) and cubicwebversion >= (3, 9, 6):
+if applcubicwebversion < (3, 9, 6) and cubicwebversion >= (3, 9, 6) and not 'CWUniqueTogetherConstraint' in schema:
add_entity_type('CWUniqueTogetherConstraint')
if not ('CWUniqueTogetherConstraint', 'CWRType') in schema['relations'].rdefs:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/scripts/chpasswd.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,48 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import getpass
+
+from cubicweb import Binary
+from cubicweb.server.utils import crypt_password
+
+
+if __args__:
+ login = __args__.pop()
+else:
+ login = raw_input("login ? ")
+
+rset = rql('Any U WHERE U is CWUser, U login %(login)s', {'login': login})
+
+if len(rset) != 1:
+ sys.exit("user '%s' does not exist!" % login)
+
+pass1 = getpass.getpass(prompt='Enter new password ? ')
+pass2 = getpass.getpass(prompt='Confirm ? ')
+
+if pass1 != pass2:
+ sys.exit("passwords don't match!")
+
+crypted = crypt_password(pass1)
+
+cwuser = rset.get_entity(0,0)
+cwuser.set_attributes(upassword=Binary(crypted))
+commit()
+
+print("password updated.")
--- a/req.py Mon May 16 16:24:00 2011 +0200
+++ b/req.py Wed Jul 20 18:21:47 2011 +0200
@@ -88,7 +88,7 @@
rset = ResultSet([('A',)]*size, '%s X' % etype,
description=[(etype,)]*size)
def get_entity(row, col=0, etype=etype, req=self, rset=rset):
- return req.vreg.etype_class(etype)(req, rset, row, col)
+ return req.vreg['etypes'].etype_class(etype)(req, rset, row, col)
rset.get_entity = get_entity
rset.req = self
return rset
--- a/rqlrewrite.py Mon May 16 16:24:00 2011 +0200
+++ b/rqlrewrite.py Wed Jul 20 18:21:47 2011 +0200
@@ -20,12 +20,16 @@
This is used for instance for read security checking in the repository.
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
from rql import nodes as n, stmts, TypeResolverException
from rql.utils import common_parent
+
from yams import BadSchemaDefinition
+
+from logilab.common import tempattr
from logilab.common.graph import has_path
from cubicweb import Unauthorized, typed_eid
@@ -156,7 +160,6 @@
self.exists_snippet = {}
self.pending_keys = []
self.existingvars = existingvars
- self._insert_scope = None
# we have to annotate the rqlst before inserting snippets, even though
# we'll have to redo it latter
self.annotate(select)
@@ -193,6 +196,7 @@
self.varmap = varmap
self.revvarmap = {}
self.varinfos = []
+ self._insert_scope = None
for i, (selectvar, snippetvar) in enumerate(varmap):
assert snippetvar in 'SOX'
self.revvarmap[snippetvar] = (selectvar, i)
@@ -229,7 +233,7 @@
except Unsupported:
continue
inserted = True
- if new is not None:
+ if new is not None and self._insert_scope is None:
self.exists_snippet[rqlexpr] = new
parent = parent or new
else:
@@ -263,11 +267,12 @@
insert_scope = common_parent(scope, insert_scope)
else:
insert_scope = self._insert_scope
- if any(vi.get('stinfo', {}).get('optrelations') for vi in self.varinfos):
+ if self._insert_scope is None and any(vi.get('stinfo', {}).get('optrelations')
+ for vi in self.varinfos):
assert parent is None
self._insert_scope = self.snippet_subquery(varmap, new)
self.insert_pending()
- self._insert_scope = None
+ #self._insert_scope = None
return
if not isinstance(new, (n.Exists, n.Not)):
new = n.Exists(new)
@@ -283,15 +288,14 @@
except Unsupported:
# some solutions have been lost, can't apply this rql expr
if parent is None:
- self.select.remove_node(new, undefine=True)
+ self.current_statement().remove_node(new, undefine=True)
else:
parent.parent.replace(or_, or_.children[0])
self._cleanup_inserted(new)
raise
else:
- self._insert_scope = new
- self.insert_pending()
- self._insert_scope = None
+ with tempattr(self, '_insert_scope', new):
+ self.insert_pending()
return new
self.insert_pending()
@@ -303,6 +307,7 @@
recomputed, we have to insert snippet defined for <action> of entity
types taken by X
"""
+ stmt = self.current_statement()
while self.pending_keys:
key, action = self.pending_keys.pop()
try:
@@ -313,12 +318,12 @@
except KeyError:
# variable isn't used anywhere else, we can't insert security
raise Unauthorized()
- ptypes = self.select.defined_vars[varname].stinfo['possibletypes']
+ ptypes = stmt.defined_vars[varname].stinfo['possibletypes']
if len(ptypes) > 1:
# XXX dunno how to handle this
self.session.error(
'cant check security of %s, ambigous type for %s in %s',
- self.select, varname, key[0]) # key[0] == the rql expression
+ stmt, varname, key[0]) # key[0] == the rql expression
raise Unauthorized()
etype = iter(ptypes).next()
eschema = self.schema.eschema(etype)
@@ -462,6 +467,7 @@
original query, return that relation node
"""
rschema = self.schema.rschema(sniprel.r_type)
+ stmt = self.current_statement()
for vi in self.varinfos:
try:
if target == 'object':
@@ -477,6 +483,9 @@
except KeyError:
# may be raised by vi['xhs_rels'][sniprel.r_type]
return None
+ # don't share if relation's statement is not the current statement
+ if orel.stmt is not stmt:
+ return None
# can't share neged relation or relations with different outer join
if (orel.neged(strict=True) or sniprel.neged(strict=True)
or (orel.optional and orel.optional != sniprel.optional)):
@@ -493,23 +502,25 @@
def _use_orig_term(self, snippet_varname, term):
key = (self.current_expr, self.varmap, snippet_varname)
if key in self.rewritten:
- insertedvar = self.select.defined_vars.pop(self.rewritten[key])
+ stmt = self.current_statement()
+ insertedvar = stmt.defined_vars.pop(self.rewritten[key])
for inserted_vref in insertedvar.references():
- inserted_vref.parent.replace(inserted_vref, term.copy(self.select))
+ inserted_vref.parent.replace(inserted_vref, term.copy(stmt))
self.rewritten[key] = term.name
def _get_varname_or_term(self, vname):
+ stmt = self.current_statement()
if vname == 'U':
+ stmt = self.select
if self.u_varname is None:
- select = self.select
- self.u_varname = select.allocate_varname()
+ self.u_varname = stmt.allocate_varname()
# generate an identifier for the substitution
- argname = select.allocate_varname()
+ argname = stmt.allocate_varname()
while argname in self.kwargs:
- argname = select.allocate_varname()
+ argname = stmt.allocate_varname()
# insert "U eid %(u)s"
- select.add_constant_restriction(
- select.get_variable(self.u_varname),
+ stmt.add_constant_restriction(
+ stmt.get_variable(self.u_varname),
'eid', unicode(argname), 'Substitute')
self.kwargs[argname] = self.session.user.eid
return self.u_varname
@@ -517,7 +528,7 @@
try:
return self.rewritten[key]
except KeyError:
- self.rewritten[key] = newvname = self.select.allocate_varname()
+ self.rewritten[key] = newvname = stmt.allocate_varname()
return newvname
# visitor methods ##########################################################
@@ -625,14 +636,20 @@
def visit_variableref(self, node):
"""get the sql name for a variable reference"""
+ stmt = self.current_statement()
if node.name in self.revvarmap:
selectvar, index = self.revvarmap[node.name]
vi = self.varinfos[index]
if vi.get('const') is not None:
return n.Constant(vi['const'], 'Int') # XXX gae
- return n.VariableRef(self.select.get_variable(selectvar))
+ return n.VariableRef(stmt.get_variable(selectvar))
vname_or_term = self._get_varname_or_term(node.name)
if isinstance(vname_or_term, basestring):
- return n.VariableRef(self.select.get_variable(vname_or_term))
+ return n.VariableRef(stmt.get_variable(vname_or_term))
# shared term
- return vname_or_term.copy(self.select)
+ return vname_or_term.copy(stmt)
+
+ def current_statement(self):
+ if self._insert_scope is None:
+ return self.select
+ return self._insert_scope.stmt
--- a/rset.py Mon May 16 16:24:00 2011 +0200
+++ b/rset.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -79,7 +79,7 @@
rows = rows[:10] + ['...']
if len(rows) > 1:
# add a line break before first entity if more that one.
- pattern = '<resultset %r (%s rows):\n%s>'
+ pattern = '<resultset %r (%s rows):\n%s>'
else:
pattern = '<resultset %r (%s rows): %s>'
@@ -515,17 +515,15 @@
@cached
def syntax_tree(self):
- """get the syntax tree for the source query.
-
- :rtype: rql.stmts.Statement
- :return: the RQL syntax tree of the originating query
+ """return the syntax tree (:class:`rql.stmts.Union`) for the originating
+ query. You can expect it to have solutions computed but it won't be
+ annotated (you usually don't need that for simple introspection).
"""
if self._rqlst:
rqlst = self._rqlst.copy()
# to avoid transport overhead when pyro is used, the schema has been
# unset from the syntax tree
rqlst.schema = self.req.vreg.schema
- self.req.vreg.rqlhelper.annotate(rqlst)
else:
rqlst = self.req.vreg.parse(self.req, self.rql, self.args)
return rqlst
@@ -673,8 +671,12 @@
root = rootselect.parent
selectmain = select.selection[selectidx]
for i, term in enumerate(rootselect.selection):
- rootvar = _get_variable(term)
- if rootvar is None:
+ try:
+ # don't use _get_variable here: if the term isn't a variable
+ # (function...), we don't want it to be used as an entity attribute
+ # or relation's value (XXX beside MAX/MIN trick?)
+ rootvar = term.variable
+ except AttributeError:
continue
if rootvar.name == rootmainvar.name:
continue
--- a/schema.py Mon May 16 16:24:00 2011 +0200
+++ b/schema.py Wed Jul 20 18:21:47 2011 +0200
@@ -28,6 +28,7 @@
from logilab.common.decorators import cached, clear_cache, monkeypatch
from logilab.common.logging_ext import set_log_methods
from logilab.common.deprecation import deprecated, class_moved
+from logilab.common.textutils import splitstrip
from logilab.common.graph import get_cycles
from logilab.common.compat import any
@@ -179,35 +180,6 @@
__builtins__['display_name'] = deprecated('[3.4] display_name should be imported from cubicweb.schema')(display_name)
-# rql expression utilities function ############################################
-
-def guess_rrqlexpr_mainvars(expression):
- defined = set(split_expression(expression))
- mainvars = []
- if 'S' in defined:
- mainvars.append('S')
- if 'O' in defined:
- mainvars.append('O')
- if 'U' in defined:
- mainvars.append('U')
- if not mainvars:
- raise Exception('unable to guess selection variables')
- return ','.join(sorted(mainvars))
-
-def split_expression(rqlstring):
- for expr in rqlstring.split(','):
- for noparen in expr.split('('):
- for word in noparen.split():
- yield word
-
-def normalize_expression(rqlstring):
- """normalize an rql expression to ease schema synchronization (avoid
- suppressing and reinserting an expression if only a space has been added/removed
- for instance)
- """
- return u', '.join(' '.join(expr.split()) for expr in rqlstring.split(','))
-
-
# Schema objects definition ###################################################
def ERSchema_display_name(self, req, form='', context=None):
@@ -406,11 +378,27 @@
def add_subject_relation(self, rschema):
"""register the relation schema as possible subject relation"""
super(CubicWebEntitySchema, self).add_subject_relation(rschema)
- self._update_has_text()
+ if rschema.final:
+ if self.rdef(rschema).get('fulltextindexed'):
+ self._update_has_text()
+ elif rschema.fulltext_container:
+ self._update_has_text()
+
+ def add_object_relation(self, rschema):
+ """register the relation schema as possible object relation"""
+ super(CubicWebEntitySchema, self).add_object_relation(rschema)
+ if rschema.fulltext_container:
+ self._update_has_text()
def del_subject_relation(self, rtype):
super(CubicWebEntitySchema, self).del_subject_relation(rtype)
- self._update_has_text(True)
+ if 'has_text' in self.subjrels:
+ self._update_has_text(deletion=True)
+
+ def del_object_relation(self, rtype):
+ super(CubicWebEntitySchema, self).del_object_relation(rtype)
+ if 'has_text' in self.subjrels:
+ self._update_has_text(deletion=True)
def _update_has_text(self, deletion=False):
may_need_has_text, has_has_text = False, False
@@ -640,175 +628,58 @@
def schema_by_eid(self, eid):
return self._eid_index[eid]
-
-# Possible constraints ########################################################
-
-class BaseRQLConstraint(BaseConstraint):
- """base class for rql constraints
- """
- distinct_query = None
-
- def __init__(self, restriction, mainvars=None):
- self.restriction = normalize_expression(restriction)
- if mainvars is None:
- mainvars = guess_rrqlexpr_mainvars(restriction)
- else:
- normmainvars = []
- for mainvar in mainvars.split(','):
- mainvar = mainvar.strip()
- if not mainvar.isalpha():
- raise Exception('bad mainvars %s' % mainvars)
- normmainvars.append(mainvar)
- assert mainvars, 'bad mainvars %s' % mainvars
- mainvars = ','.join(sorted(normmainvars))
- self.mainvars = mainvars
-
- def serialize(self):
- # start with a comma for bw compat, see below
- return ';' + self.mainvars + ';' + self.restriction
-
- @classmethod
- def deserialize(cls, value):
- # XXX < 3.5.10 bw compat
- if not value.startswith(';'):
- return cls(value)
- _, mainvars, restriction = value.split(';', 2)
- return cls(restriction, mainvars)
-
- def check(self, entity, rtype, value):
- """return true if the value satisfy the constraint, else false"""
- # implemented as a hook in the repository
- return 1
-
- def repo_check(self, session, eidfrom, rtype, eidto):
- """raise ValidationError if the relation doesn't satisfy the constraint
- """
- pass # this is a vocabulary constraint, not enforce XXX why?
-
- def __str__(self):
- if self.distinct_query:
- selop = 'Any'
- else:
- selop = 'DISTINCT Any'
- return '%s(%s %s WHERE %s)' % (self.__class__.__name__, selop,
- self.mainvars, self.restriction)
-
- def __repr__(self):
- return '<%s @%#x>' % (self.__str__(), id(self))
-
-
-class RQLVocabularyConstraint(BaseRQLConstraint):
- """the rql vocabulary constraint :
-
- limit the proposed values to a set of entities returned by a rql query,
- but this is not enforced at the repository level
-
- restriction is additional rql restriction that will be added to
- a predefined query, where the S and O variables respectivly represent
- the subject and the object of the relation
-
- mainvars is a string that should be used as selection variable (eg
- `'Any %s WHERE ...' % mainvars`). If not specified, an attempt will be
- done to guess it according to variable used in the expression.
- """
-
-
-class RepoEnforcedRQLConstraintMixIn(object):
+# Bases for manipulating RQL in schema #########################################
- def __init__(self, restriction, mainvars=None, msg=None):
- super(RepoEnforcedRQLConstraintMixIn, self).__init__(restriction, mainvars)
- self.msg = msg
-
- def serialize(self):
- # start with a semicolon for bw compat, see below
- return ';%s;%s\n%s' % (self.mainvars, self.restriction,
- self.msg or '')
-
- def deserialize(cls, value):
- # XXX < 3.5.10 bw compat
- if not value.startswith(';'):
- return cls(value)
- value, msg = value.split('\n', 1)
- _, mainvars, restriction = value.split(';', 2)
- return cls(restriction, mainvars, msg)
- deserialize = classmethod(deserialize)
+def guess_rrqlexpr_mainvars(expression):
+ defined = set(split_expression(expression))
+ mainvars = set()
+ if 'S' in defined:
+ mainvars.add('S')
+ if 'O' in defined:
+ mainvars.add('O')
+ if 'U' in defined:
+ mainvars.add('U')
+ if not mainvars:
+ raise Exception('unable to guess selection variables')
+ return mainvars
- def repo_check(self, session, eidfrom, rtype, eidto=None):
- """raise ValidationError if the relation doesn't satisfy the constraint
- """
- if not self.match_condition(session, eidfrom, eidto):
- # XXX at this point if both or neither of S and O are in mainvar we
- # dunno if the validation error `occurred` on eidfrom or eidto (from
- # user interface point of view)
- #
- # possible enhancement: check entity being created, it's probably
- # the main eid unless this is a composite relation
- if eidto is None or 'S' in self.mainvars or not 'O' in self.mainvars:
- maineid = eidfrom
- qname = role_name(rtype, 'subject')
- else:
- maineid = eidto
- qname = role_name(rtype, 'object')
- if self.msg:
- msg = session._(self.msg)
- else:
- msg = '%(constraint)s %(restriction)s failed' % {
- 'constraint': session._(self.type()),
- 'restriction': self.restriction}
- raise ValidationError(maineid, {qname: msg})
+def split_expression(rqlstring):
+ for expr in rqlstring.split(','):
+ for noparen1 in expr.split('('):
+ for noparen2 in noparen1.split(')'):
+ for word in noparen2.split():
+ yield word
- def exec_query(self, session, eidfrom, eidto):
- if eidto is None:
- # checking constraint for an attribute relation
- restriction = 'S eid %(s)s, ' + self.restriction
- args = {'s': eidfrom}
- else:
- restriction = 'S eid %(s)s, O eid %(o)s, ' + self.restriction
- args = {'s': eidfrom, 'o': eidto}
- rql = 'Any %s WHERE %s' % (self.mainvars, restriction)
- if self.distinct_query:
- rql = 'DISTINCT ' + rql
- return session.execute(rql, args, build_descr=False)
-
-
-class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
- """the rql constraint is similar to the RQLVocabularyConstraint but
- are also enforced at the repository level
+def normalize_expression(rqlstring):
+ """normalize an rql expression to ease schema synchronization (avoid
+ suppressing and reinserting an expression if only a space has been
+ added/removed for instance)
"""
- distinct_query = False
-
- def match_condition(self, session, eidfrom, eidto):
- return self.exec_query(session, eidfrom, eidto)
-
-
-class RQLUniqueConstraint(RepoEnforcedRQLConstraintMixIn, BaseRQLConstraint):
- """the unique rql constraint check that the result of the query isn't
- greater than one.
-
- You *must* specify mainvars when instantiating the constraint since there is
- no way to guess it correctly (e.g. if using S,O or U the constraint will
- always be satisfied because we've to use a DISTINCT query).
- """
- # XXX turns mainvars into a required argument in __init__
- distinct_query = True
-
- def match_condition(self, session, eidfrom, eidto):
- return len(self.exec_query(session, eidfrom, eidto)) <= 1
+ return u', '.join(' '.join(expr.split()) for expr in rqlstring.split(','))
class RQLExpression(object):
+ """Base class for RQL expression used in schema (constraints and
+ permissions)
+ """
+ # these are overridden by set_log_methods below
+ # only defining here to prevent pylint from complaining
+ info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
def __init__(self, expression, mainvars, eid):
self.eid = eid # eid of the entity representing this rql expression
- if not isinstance(mainvars, unicode):
- mainvars = unicode(mainvars)
+ assert mainvars, 'bad mainvars %s' % mainvars
+ if isinstance(mainvars, basestring):
+ mainvars = set(splitstrip(mainvars))
+ elif not isinstance(mainvars, set):
+ mainvars = set(mainvars)
self.mainvars = mainvars
self.expression = normalize_expression(expression)
try:
self.rqlst = parse(self.full_rql, print_errors=False).children[0]
except RQLSyntaxError:
raise RQLSyntaxError(expression)
- for mainvar in mainvars.split(','):
+ for mainvar in mainvars:
if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
_LOGGER.warn('You did not use the %s variable in your RQL '
'expression %s', mainvar, self)
@@ -832,6 +703,8 @@
def __setstate__(self, state):
self.__init__(*state)
+ # permission rql expression specific stuff #################################
+
@cached
def transform_has_permission(self):
found = None
@@ -942,12 +815,10 @@
@property
def minimal_rql(self):
- return 'Any %s WHERE %s' % (self.mainvars, self.expression)
+ return 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)),
+ self.expression)
- # these are overridden by set_log_methods below
- # only defining here to prevent pylint from complaining
- info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
-
+# rql expressions for use in permission definition #############################
class ERQLExpression(RQLExpression):
def __init__(self, expression, mainvars=None, eid=None):
@@ -1024,12 +895,156 @@
kwargs['o'] = toeid
return self._check(session, **kwargs)
+
# in yams, default 'update' perm for attributes granted to managers and owners.
# Within cw, we want to default to users who may edit the entity holding the
# attribute.
ybo.DEFAULT_ATTRPERMS['update'] = (
'managers', ERQLExpression('U has_update_permission X'))
+# additional cw specific constraints ###########################################
+
+class BaseRQLConstraint(RRQLExpression, BaseConstraint):
+ """base class for rql constraints"""
+ distinct_query = None
+
+ def serialize(self):
+ # start with a comma for bw compat,see below
+ return ';' + ','.join(sorted(self.mainvars)) + ';' + self.expression
+
+ @classmethod
+ def deserialize(cls, value):
+ # XXX < 3.5.10 bw compat
+ if not value.startswith(';'):
+ return cls(value)
+ _, mainvars, expression = value.split(';', 2)
+ return cls(expression, mainvars)
+
+ def check(self, entity, rtype, value):
+ """return true if the value satisfy the constraint, else false"""
+ # implemented as a hook in the repository
+ return 1
+
+ def __str__(self):
+ if self.distinct_query:
+ selop = 'Any'
+ else:
+ selop = 'DISTINCT Any'
+ return '%s(%s %s WHERE %s)' % (self.__class__.__name__, selop,
+ ','.join(sorted(self.mainvars)),
+ self.expression)
+
+ def __repr__(self):
+ return '<%s @%#x>' % (self.__str__(), id(self))
+
+
+class RQLVocabularyConstraint(BaseRQLConstraint):
+ """the rql vocabulary constraint:
+
+ limit the proposed values to a set of entities returned by a rql query,
+ but this is not enforced at the repository level
+
+ `expression` is additional rql restriction that will be added to
+ a predefined query, where the S and O variables respectivly represent
+ the subject and the object of the relation
+
+ `mainvars` is a set of variables that should be used as selection variable
+ (eg `'Any %s WHERE ...' % mainvars`). If not specified, an attempt will be
+ done to guess it according to variable used in the expression.
+ """
+
+ def repo_check(self, session, eidfrom, rtype, eidto):
+ """raise ValidationError if the relation doesn't satisfy the constraint
+ """
+ pass # this is a vocabulary constraint, not enforce
+
+
+class RepoEnforcedRQLConstraintMixIn(object):
+
+ def __init__(self, expression, mainvars=None, msg=None):
+ super(RepoEnforcedRQLConstraintMixIn, self).__init__(expression, mainvars)
+ self.msg = msg
+
+ def serialize(self):
+ # start with a semicolon for bw compat, see below
+ return ';%s;%s\n%s' % (','.join(sorted(self.mainvars)), self.expression,
+ self.msg or '')
+
+ def deserialize(cls, value):
+ # XXX < 3.5.10 bw compat
+ if not value.startswith(';'):
+ return cls(value)
+ value, msg = value.split('\n', 1)
+ _, mainvars, expression = value.split(';', 2)
+ return cls(expression, mainvars, msg)
+ deserialize = classmethod(deserialize)
+
+ def repo_check(self, session, eidfrom, rtype, eidto=None):
+ """raise ValidationError if the relation doesn't satisfy the constraint
+ """
+ if not self.match_condition(session, eidfrom, eidto):
+ # XXX at this point if both or neither of S and O are in mainvar we
+ # dunno if the validation error `occurred` on eidfrom or eidto (from
+ # user interface point of view)
+ #
+ # possible enhancement: check entity being created, it's probably
+ # the main eid unless this is a composite relation
+ if eidto is None or 'S' in self.mainvars or not 'O' in self.mainvars:
+ maineid = eidfrom
+ qname = role_name(rtype, 'subject')
+ else:
+ maineid = eidto
+ qname = role_name(rtype, 'object')
+ if self.msg:
+ msg = session._(self.msg)
+ else:
+ msg = '%(constraint)s %(expression)s failed' % {
+ 'constraint': session._(self.type()),
+ 'expression': self.expression}
+ raise ValidationError(maineid, {qname: msg})
+
+ def exec_query(self, session, eidfrom, eidto):
+ if eidto is None:
+ # checking constraint for an attribute relation
+ expression = 'S eid %(s)s, ' + self.expression
+ args = {'s': eidfrom}
+ else:
+ expression = 'S eid %(s)s, O eid %(o)s, ' + self.expression
+ args = {'s': eidfrom, 'o': eidto}
+ if 'U' in self.rqlst.defined_vars:
+ expression = 'U eid %(u)s, ' + expression
+ args['u'] = session.user.eid
+ rql = 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)), expression)
+ if self.distinct_query:
+ rql = 'DISTINCT ' + rql
+ return session.execute(rql, args, build_descr=False)
+
+
+class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
+ """the rql constraint is similar to the RQLVocabularyConstraint but
+ are also enforced at the repository level
+ """
+ distinct_query = False
+
+ def match_condition(self, session, eidfrom, eidto):
+ return self.exec_query(session, eidfrom, eidto)
+
+
+class RQLUniqueConstraint(RepoEnforcedRQLConstraintMixIn, BaseRQLConstraint):
+ """the unique rql constraint check that the result of the query isn't
+ greater than one.
+
+ You *must* specify `mainvars` when instantiating the constraint since there
+ is no way to guess it correctly (e.g. if using S,O or U the constraint will
+ always be satisfied because we've to use a DISTINCT query).
+ """
+ # XXX turns mainvars into a required argument in __init__
+ distinct_query = True
+
+ def match_condition(self, session, eidfrom, eidto):
+ return len(self.exec_query(session, eidfrom, eidto)) <= 1
+
+
# workflow extensions #########################################################
from yams.buildobjs import _add_relation as yams_add_relation
--- a/selectors.py Mon May 16 16:24:00 2011 +0200
+++ b/selectors.py Wed Jul 20 18:21:47 2011 +0200
@@ -196,7 +196,7 @@
from warnings import warn
from operator import eq
-from logilab.common.deprecation import class_renamed
+from logilab.common.deprecation import class_renamed, deprecated
from logilab.common.compat import all, any
from logilab.common.interface import implements as implements_iface
@@ -1160,9 +1160,12 @@
See :class:`~cubicweb.selectors.EntitySelector` documentation for entity
lookup / score rules according to the input context.
"""
- def __init__(self, expression, once_is_enough=False):
+ def __init__(self, expression, once_is_enough=False, user_condition=False):
super(rql_condition, self).__init__(once_is_enough)
- if 'U' in frozenset(split_expression(expression)):
+ self.user_condition = user_condition
+ if user_condition:
+ rql = 'Any COUNT(U) WHERE U eid %%(u)s, %s' % expression
+ elif 'U' in frozenset(split_expression(expression)):
rql = 'Any COUNT(X) WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression
else:
rql = 'Any COUNT(X) WHERE X eid %%(x)s, %s' % expression
@@ -1171,13 +1174,30 @@
def __str__(self):
return '%s(%r)' % (self.__class__.__name__, self.rql)
- def score(self, req, rset, row, col):
+ @lltrace
+ def __call__(self, cls, req, **kwargs):
+ if self.user_condition:
+ try:
+ return req.execute(self.rql, {'u': req.user.eid})[0][0]
+ except Unauthorized:
+ return 0
+ else:
+ return super(rql_condition, self).__call__(cls, req, **kwargs)
+
+ def _score(self, req, eid):
try:
- return req.execute(self.rql, {'x': rset[row][col],
- 'u': req.user.eid})[0][0]
+ return req.execute(self.rql, {'x': eid, 'u': req.user.eid})[0][0]
except Unauthorized:
return 0
+ def score(self, req, rset, row, col):
+ return self._score(req, rset[row][col])
+
+ def score_entity(self, entity):
+ return self._score(entity._cw, entity.eid)
+
+
+# workflow selectors ###########################################################
class is_in_state(score_entity):
"""Return 1 if entity is in one of the states given as argument list
@@ -1189,9 +1209,8 @@
* you must use the latest tr info thru the workflow adapter for repository
side checking of the current state
- In debug mode, this selector can raise:
- :raises: :exc:`ValueError` for unknown states names
- (etype workflow only not checked in custom workflow)
+ In debug mode, this selector can raise :exc:`ValueError` for unknown states names
+ (only checked on entities without a custom workflow)
:rtype: int
"""
@@ -1231,40 +1250,49 @@
','.join(str(s) for s in self.expected))
-class on_transition(is_in_state):
- """Return 1 if entity is in one of the transitions given as argument list
+def on_fire_transition(etype, tr_name, from_state_name=None):
+ """Return 1 when entity of the type `etype` is going through transition of
+ the name `tr_name`.
- Especially useful to match passed transition to enable notifications when
- your workflow allows several transition to the same states.
+ If `from_state_name` is specified, this selector will also check the
+ incoming state.
- Note that if workflow `change_state` adapter method is used, this selector
- will not be triggered.
+ You should use this selector on 'after_add_entity' hook, since it's actually
+ looking for addition of `TrInfo` entities. Hence in the hook, `self.entity`
+ will reference the matching `TrInfo` entity, allowing to get all the
+ transition details (including the entity to which is applied the transition
+ but also its original state, transition, destination state, user...).
- You should use this instead of your own :class:`score_entity` selector to
- avoid some gotchas:
-
- * possible views gives a fake entity with no state
- * you must use the latest tr info thru the workflow adapter for repository
- side checking of the current state
+ See :class:`cubicweb.entities.wfobjs.TrInfo` for more information.
+ """
+ def match_etype_and_transition(trinfo):
+ # take care trinfo.transition is None when calling change_state
+ return (trinfo.transition and trinfo.transition.name == tr_name
+ # is_instance() first two arguments are 'cls' (unused, so giving
+ # None is fine) and the request/session
+ and is_instance(etype)(None, trinfo._cw, entity=trinfo.for_entity))
- In debug mode, this selector can raise:
- :raises: :exc:`ValueError` for unknown transition names
- (etype workflow only not checked in custom workflow)
+ return is_instance('TrInfo') & score_entity(match_etype_and_transition)
+
- :rtype: int
+class match_transition(ExpectedValueSelector):
+ """Return 1 if `transition` argument is found in the input context which has
+ a `.name` attribute matching one of the expected names given to the
+ initializer.
+
+ This selector is expected to be used to customise the status change form in
+ the web ui.
"""
- def _score(self, adapted):
- trinfo = adapted.latest_trinfo()
- if trinfo and trinfo.by_transition:
- return trinfo.by_transition[0].name in self.expected
-
- def _validate(self, adapted):
- wf = adapted.current_workflow
- valid = [n.name for n in wf.reverse_transition_of]
- unknown = sorted(self.expected.difference(valid))
- if unknown:
- raise ValueError("%s: unknown transition(s): %s"
- % (wf.name, ",".join(unknown)))
+ @lltrace
+ def __call__(self, cls, req, transition=None, **kwargs):
+ # XXX check this is a transition that apply to the object?
+ if transition is None:
+ treid = req.form.get('treid', None)
+ if treid:
+ transition = req.entity_from_eid(treid)
+ if transition is not None and getattr(transition, 'name', None) in self.expected:
+ return 1
+ return 0
# logged user selectors ########################################################
@@ -1317,6 +1345,8 @@
@lltrace
def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs):
+ if not getattr(req, 'cnx', True): # default to True for repo session instances
+ return 0
user = req.user
if user is None:
return int('guests' in self.expected)
@@ -1504,23 +1534,6 @@
# Other selectors ##############################################################
-# XXX deprecated ? maybe use on_transition selector instead ?
-class match_transition(ExpectedValueSelector):
- """Return 1 if `transition` argument is found in the input context which has
- a `.name` attribute matching one of the expected names given to the
- initializer.
- """
- @lltrace
- def __call__(self, cls, req, transition=None, **kwargs):
- # XXX check this is a transition that apply to the object?
- if transition is None:
- treid = req.form.get('treid', None)
- if treid:
- transition = req.entity_from_eid(treid)
- if transition is not None and getattr(transition, 'name', None) in self.expected:
- return 1
- return 0
-
class match_exception(ExpectedValueSelector):
"""Return 1 if a view is specified an as its registry id is in one of the
@@ -1544,6 +1557,47 @@
## deprecated stuff ############################################################
+
+class on_transition(is_in_state):
+ """Return 1 if entity is in one of the transitions given as argument list
+
+ Especially useful to match passed transition to enable notifications when
+ your workflow allows several transition to the same states.
+
+ Note that if workflow `change_state` adapter method is used, this selector
+ will not be triggered.
+
+ You should use this instead of your own :class:`score_entity` selector to
+ avoid some gotchas:
+
+ * possible views gives a fake entity with no state
+ * you must use the latest tr info thru the workflow adapter for repository
+ side checking of the current state
+
+ In debug mode, this selector can raise:
+ :raises: :exc:`ValueError` for unknown transition names
+ (etype workflow only not checked in custom workflow)
+
+ :rtype: int
+ """
+ @deprecated('[3.12] on_transition is deprecated, you should rather use '
+ 'on_fire_transition(etype, trname)')
+ def __init__(self, *expected):
+ super(on_transition, self).__init__(*expected)
+
+ def _score(self, adapted):
+ trinfo = adapted.latest_trinfo()
+ if trinfo and trinfo.by_transition:
+ return trinfo.by_transition[0].name in self.expected
+
+ def _validate(self, adapted):
+ wf = adapted.current_workflow
+ valid = [n.name for n in wf.reverse_transition_of]
+ unknown = sorted(self.expected.difference(valid))
+ if unknown:
+ raise ValueError("%s: unknown transition(s): %s"
+ % (wf.name, ",".join(unknown)))
+
entity_implements = class_renamed('entity_implements', is_instance)
class _but_etype(EntitySelector):
--- a/server/__init__.py Mon May 16 16:24:00 2011 +0200
+++ b/server/__init__.py Wed Jul 20 18:21:47 2011 +0200
@@ -18,7 +18,7 @@
"""Server subcube of cubicweb : defines objects used only on the server
(repository) side
-This module contains functions to initialize a new repository.
+The server module contains functions to initialize a new repository.
"""
from __future__ import with_statement
@@ -36,17 +36,32 @@
from cubicweb import CW_SOFTWARE_ROOT
+class ShuttingDown(BaseException):
+ """raised when trying to access some resources while the repository is
+ shutting down. Inherit from BaseException so that `except Exception` won't
+ catch it.
+ """
+
# server-side debugging #########################################################
# server debugging flags. They may be combined using binary operators.
-DBG_NONE = 0 # no debug information
-DBG_RQL = 1 # rql execution information
-DBG_SQL = 2 # executed sql
-DBG_REPO = 4 # repository events
-DBG_MS = 8 # multi-sources
-DBG_MORE = 16 # more verbosity
-DBG_ALL = 1 + 2 + 4 + 8 + 16
-# current debug mode
+
+#:no debug information
+DBG_NONE = 0 #: no debug information
+#: rql execution information
+DBG_RQL = 1
+#: executed sql
+DBG_SQL = 2
+#: repository events
+DBG_REPO = 4
+#: multi-sources
+DBG_MS = 8
+#: more verbosity
+DBG_MORE = 16
+#: all level enabled
+DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_MORE
+
+#: current debug mode
DEBUG = 0
def set_debug(debugmode):
--- a/server/checkintegrity.py Mon May 16 16:24:00 2011 +0200
+++ b/server/checkintegrity.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -30,7 +30,7 @@
from logilab.common.shellutils import ProgressBar
-from cubicweb.schema import PURE_VIRTUAL_RTYPES
+from cubicweb.schema import PURE_VIRTUAL_RTYPES, VIRTUAL_RTYPES
from cubicweb.server.sqlutils import SQL_PREFIX
from cubicweb.server.session import security_enabled
--- a/server/hook.py Mon May 16 16:24:00 2011 +0200
+++ b/server/hook.py Wed Jul 20 18:21:47 2011 +0200
@@ -270,13 +270,17 @@
'session_open', 'session_close'))
ALL_HOOKS = ENTITIES_HOOKS | RELATIONS_HOOKS | SYSTEM_HOOKS
-def _iter_kwargs(entities, kwargs):
- if not entities:
+def _iter_kwargs(entities, eids_from_to, kwargs):
+ if not entities and not eids_from_to:
yield kwargs
- else:
+ elif entities:
for entity in entities:
kwargs['entity'] = entity
yield kwargs
+ else:
+ for subject, object in eids_from_to:
+ kwargs.update({'eidfrom': subject, 'eidto': object})
+ yield kwargs
class HooksRegistry(CWRegistry):
@@ -304,12 +308,19 @@
if 'entities' in kwargs:
assert 'entity' not in kwargs, \
'can\'t pass "entities" and "entity" arguments simultaneously'
+ assert 'eids_from_to' not in kwargs, \
+ 'can\'t pass "entities" and "eids_from_to" arguments simultaneously'
entities = kwargs.pop('entities')
+ eids_from_to = []
+ elif 'eids_from_to' in kwargs:
+ entities = []
+ eids_from_to = kwargs.pop('eids_from_to')
else:
entities = []
+ eids_from_to = []
# by default, hooks are executed with security turned off
with security_enabled(session, read=False):
- for _kwargs in _iter_kwargs(entities, kwargs):
+ for _kwargs in _iter_kwargs(entities, eids_from_to, kwargs):
hooks = sorted(self.possible_objects(session, **_kwargs),
key=lambda x: x.order)
with security_enabled(session, write=False):
@@ -841,6 +852,13 @@
def _build_container(self):
return self.containercls()
+ def union(self, data):
+ """only when container is a set"""
+ assert not self._processed, """Trying to add data to a closed operation.
+Iterating over operation data closed it and should be reserved to precommit /
+postcommit method of the operation."""
+ self._container |= data
+
def add_data(self, data):
assert not self._processed, """Trying to add data to a closed operation.
Iterating over operation data closed it and should be reserved to precommit /
--- a/server/migractions.py Mon May 16 16:24:00 2011 +0200
+++ b/server/migractions.py Wed Jul 20 18:21:47 2011 +0200
@@ -162,7 +162,7 @@
# server specific migration methods ########################################
- def backup_database(self, backupfile=None, askconfirm=True):
+ def backup_database(self, backupfile=None, askconfirm=True, format='native'):
config = self.config
repo = self.repo_connect()
# paths
@@ -185,16 +185,24 @@
# backup
tmpdir = tempfile.mkdtemp()
try:
+ failed = False
for source in repo.sources:
try:
- source.backup(osp.join(tmpdir, source.uri), self.confirm)
+ source.backup(osp.join(tmpdir, source.uri), self.confirm, format=format)
except Exception, ex:
print '-> error trying to backup %s [%s]' % (source.uri, ex)
if not self.confirm('Continue anyway?', default='n'):
raise SystemExit(1)
else:
- break
- else:
+ failed = True
+ with open(osp.join(tmpdir, 'format.txt'), 'w') as format_file:
+ format_file.write('%s\n' % format)
+ with open(osp.join(tmpdir, 'versions.txt'), 'w') as version_file:
+ versions = repo.get_versions()
+ for cube, version in versions.iteritems():
+ version_file.write('%s %s\n' % (cube, version))
+
+ if not failed:
bkup = tarfile.open(backupfile, 'w|gz')
for filename in os.listdir(tmpdir):
bkup.add(osp.join(tmpdir, filename), filename)
@@ -207,7 +215,7 @@
shutil.rmtree(tmpdir)
def restore_database(self, backupfile, drop=True, systemonly=True,
- askconfirm=True):
+ askconfirm=True, format='native'):
# check
if not osp.exists(backupfile):
raise ExecutionError("Backup file %s doesn't exist" % backupfile)
@@ -229,13 +237,18 @@
bkup = tarfile.open(backupfile, 'r|gz')
bkup.extractall(path=tmpdir)
bkup.close()
+ if osp.isfile(osp.join(tmpdir, 'format.txt')):
+ with open(osp.join(tmpdir, 'format.txt')) as format_file:
+ written_format = format_file.readline().strip()
+ if written_format in ('portable', 'native'):
+ format = written_format
self.config.open_connections_pools = False
repo = self.repo_connect()
for source in repo.sources:
if systemonly and source.uri != 'system':
continue
try:
- source.restore(osp.join(tmpdir, source.uri), self.confirm, drop)
+ source.restore(osp.join(tmpdir, source.uri), self.confirm, drop, format)
except Exception, exc:
print '-> error trying to restore %s [%s]' % (source.uri, exc)
if not self.confirm('Continue anyway?', default='n'):
@@ -438,7 +451,8 @@
'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
'WHERE T eid %%(x)s' % perm,
{'expr': expr, 'exprtype': exprtype,
- 'vars': expression.mainvars, 'x': teid},
+ 'vars': u','.join(sorted(expression.mainvars)),
+ 'x': teid},
ask_confirm=False)
def _synchronize_rschema(self, rtype, syncrdefs=True,
@@ -723,9 +737,15 @@
`attrname` is a string giving the name of the attribute to drop
"""
- rschema = self.repo.schema.rschema(attrname)
- attrtype = rschema.objects(etype)[0]
- self.cmd_drop_relation_definition(etype, attrname, attrtype, commit=commit)
+ try:
+ rschema = self.repo.schema.rschema(attrname)
+ attrtype = rschema.objects(etype)[0]
+ except KeyError:
+ print 'warning: attribute %s %s is not known, skip deletion' % (
+ etype, attrname)
+ else:
+ self.cmd_drop_relation_definition(etype, attrname, attrtype,
+ commit=commit)
def cmd_rename_attribute(self, etype, oldname, newname, commit=True):
"""rename an existing attribute of the given entity type
@@ -757,9 +777,10 @@
targeted type is known
"""
instschema = self.repo.schema
- assert not etype in instschema, \
- '%s already defined in the instance schema' % etype
eschema = self.fs_schema.eschema(etype)
+ if etype in instschema and not (eschema.final and eschema.eid is None):
+ print 'warning: %s already known, skip addition' % etype
+ return
confirm = self.verbosity >= 2
groupmap = self.group_mapping()
cstrtypemap = self.cstrtype_mapping()
@@ -930,23 +951,19 @@
# triggered by schema synchronization hooks.
session = self.session
for rdeftype in ('CWRelation', 'CWAttribute'):
- thispending = set()
- for eid, in self.sqlexec('SELECT cw_eid FROM cw_%s '
- 'WHERE cw_from_entity=%%(eid)s OR '
- ' cw_to_entity=%%(eid)s' % rdeftype,
- {'eid': oldeid}, ask_confirm=False):
- # we should add deleted eids into pending eids else we may
- # get some validation error on commit since integrity hooks
- # may think some required relation is missing... This also ensure
- # repository caches are properly cleanup
- hook.CleanupDeletedEidsCacheOp.get_instance(session).add_data(eid)
- # and don't forget to remove record from system tables
- self.repo.system_source.delete_info(
- session, session.entity_from_eid(eid, rdeftype),
- 'system', None)
- thispending.add(eid)
- self.sqlexec('DELETE FROM cw_%s '
- 'WHERE cw_from_entity=%%(eid)s OR '
+ thispending = set( (eid for eid, in self.sqlexec(
+ 'SELECT cw_eid FROM cw_%s WHERE cw_from_entity=%%(eid)s OR '
+ ' cw_to_entity=%%(eid)s' % rdeftype,
+ {'eid': oldeid}, ask_confirm=False)) )
+ # we should add deleted eids into pending eids else we may
+ # get some validation error on commit since integrity hooks
+ # may think some required relation is missing... This also ensure
+ # repository caches are properly cleanup
+ hook.CleanupDeletedEidsCacheOp.get_instance(session).union(thispending)
+ # and don't forget to remove record from system tables
+ entities = [session.entity_from_eid(eid, rdeftype) for eid in thispending]
+ self.repo.system_source.delete_info_multi(session, entities, 'system')
+ self.sqlexec('DELETE FROM cw_%s WHERE cw_from_entity=%%(eid)s OR '
'cw_to_entity=%%(eid)s' % rdeftype,
{'eid': oldeid}, ask_confirm=False)
# now we have to manually cleanup relations pointing to deleted
@@ -991,6 +1008,10 @@
"""
reposchema = self.repo.schema
+ if rtype in reposchema:
+ print 'warning: relation type %s is already known, skip addition' % (
+ rtype)
+ return
rschema = self.fs_schema.rschema(rtype)
execute = self._cw.execute
# register the relation into CWRType and insert necessary relation
@@ -1057,6 +1078,10 @@
rschema = self.fs_schema.rschema(rtype)
if not rtype in self.repo.schema:
self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
+ if (subjtype, objtype) in self.repo.schema.rschema(rtype).rdefs:
+ print 'warning: relation %s %s %s is already known, skip addition' % (
+ subjtype, rtype, objtype)
+ return
execute = self._cw.execute
rdef = self._get_rdef(rschema, subjtype, objtype)
ss.execschemarql(execute, rdef,
@@ -1127,6 +1152,10 @@
syncprops=syncprops)
else:
for etype in self.repo.schema.entities():
+ if etype.eid is None:
+ # not yet added final etype (thing to BigInt defined in
+ # yams though 3.13 migration not done yet)
+ continue
self._synchronize_eschema(etype, syncrdefs=syncrdefs,
syncprops=syncprops, syncperms=syncperms)
if commit:
@@ -1248,6 +1277,12 @@
self.commit()
return wf
+ def cmd_get_workflow_for(self, etype):
+ """return default workflow for the given entity type"""
+ rset = self.rqlexec('Workflow X WHERE ET default_workflow X, ET name %(et)s',
+ {'et': etype})
+ return rset.get_entity(0, 0)
+
# XXX remove once cmd_add_[state|transition] are removed
def _get_or_create_wf(self, etypes):
if not isinstance(etypes, (list, tuple)):
@@ -1404,7 +1439,7 @@
return self.cmd_create_entity(etype, *args, **kwargs).eid
@contextmanager
- def cmd_dropped_constraints(self, etype, attrname, cstrtype,
+ def cmd_dropped_constraints(self, etype, attrname, cstrtype=None,
droprequired=False):
"""context manager to drop constraints temporarily on fs_schema
@@ -1424,8 +1459,9 @@
rdef = self.fs_schema.eschema(etype).rdef(attrname)
original_constraints = rdef.constraints
# remove constraints
- rdef.constraints = [cstr for cstr in original_constraints
- if not (cstrtype and isinstance(cstr, cstrtype))]
+ if cstrtype:
+ rdef.constraints = [cstr for cstr in original_constraints
+ if not (cstrtype and isinstance(cstr, cstrtype))]
if droprequired:
original_cardinality = rdef.cardinality
rdef.cardinality = '?' + rdef.cardinality[1]
@@ -1495,13 +1531,13 @@
rschema = self.repo.schema.rschema(attr)
oldtype = rschema.objects(etype)[0]
rdefeid = rschema.rproperty(etype, oldtype, 'eid')
- sql = ("UPDATE CWAttribute "
- "SET to_entity=(SELECT eid FROM CWEType WHERE name='%s')"
- "WHERE eid=%s") % (newtype, rdefeid)
+ sql = ("UPDATE cw_CWAttribute "
+ "SET cw_to_entity=(SELECT cw_eid FROM cw_CWEType WHERE cw_name='%s')"
+ "WHERE cw_eid=%s") % (newtype, rdefeid)
self.sqlexec(sql, ask_confirm=False)
dbhelper = self.repo.system_source.dbhelper
sqltype = dbhelper.TYPE_MAPPING[newtype]
- sql = 'ALTER TABLE %s ALTER COLUMN %s TYPE %s' % (etype, attr, sqltype)
+ sql = 'ALTER TABLE cw_%s ALTER COLUMN cw_%s TYPE %s' % (etype, attr, sqltype)
self.sqlexec(sql, ask_confirm=False)
if commit:
self.commit()
--- a/server/msplanner.py Mon May 16 16:24:00 2011 +0200
+++ b/server/msplanner.py Wed Jul 20 18:21:47 2011 +0200
@@ -483,7 +483,12 @@
else:
var = vref.variable
for rel in var.stinfo['relations'] - var.stinfo['rhsrelations']:
- if rel.r_type in ('eid', 'name'):
+ # skip neged eid relation since it's the kind of query
+ # generated when clearing old value of '?1" relation,
+ # cw_source included. See
+ # unittest_ldapuser.test_copy_to_system_source
+ if rel.r_type == 'name' or \
+ (rel.r_type == 'eid' and not rel.neged(strict=True)):
if rel.r_type == 'eid':
slist = sourceeids
else:
--- a/server/mssteps.py Mon May 16 16:24:00 2011 +0200
+++ b/server/mssteps.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -22,6 +22,7 @@
* each step has is own members (this is not necessarily bad, but a bit messy
for now)
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
@@ -32,25 +33,30 @@
AGGR_TRANSFORMS = {'COUNT':'SUM', 'MIN':'MIN', 'MAX':'MAX', 'SUM': 'SUM'}
-def remove_clauses(union, keepgroup):
- clauses = []
- for select in union.children:
- if keepgroup:
- having, orderby = select.having, select.orderby
- select.having, select.orderby = (), ()
- clauses.append( (having, orderby) )
- else:
- groupby, having, orderby = select.groupby, select.having, select.orderby
- select.groupby, select.having, select.orderby = (), (), ()
- clauses.append( (groupby, having, orderby) )
- return clauses
+class remove_and_restore_clauses(object):
+ def __init__(self, union, keepgroup):
+ self.union = union
+ self.keepgroup = keepgroup
+ self.clauses = None
-def restore_clauses(union, keepgroup, clauses):
- for i, select in enumerate(union.children):
- if keepgroup:
- select.having, select.orderby = clauses[i]
- else:
- select.groupby, select.having, select.orderby = clauses[i]
+ def __enter__(self):
+ self.clauses = clauses = []
+ for select in self.union.children:
+ if self.keepgroup:
+ having, orderby = select.having, select.orderby
+ select.having, select.orderby = (), ()
+ clauses.append( (having, orderby) )
+ else:
+ groupby, having, orderby = select.groupby, select.having, select.orderby
+ select.groupby, select.having, select.orderby = (), (), ()
+ clauses.append( (groupby, having, orderby) )
+
+ def __exit__(self, exctype, exc, traceback):
+ for i, select in enumerate(self.union.children):
+ if self.keepgroup:
+ select.having, select.orderby = self.clauses[i]
+ else:
+ select.groupby, select.having, select.orderby = self.clauses[i]
class FetchStep(OneFetchStep):
@@ -94,29 +100,24 @@
plan = self.plan
plan.create_temp_table(self.table)
union = self.union
- # XXX 2.5 use "with"
- clauses = remove_clauses(union, self.keepgroup)
- for source in self.sources:
- source.flying_insert(self.table, plan.session, union, plan.args,
- self.inputmap)
- restore_clauses(union, self.keepgroup, clauses)
+ with remove_and_restore_clauses(union, self.keepgroup):
+ for source in self.sources:
+ source.flying_insert(self.table, plan.session, union, plan.args,
+ self.inputmap)
def mytest_repr(self):
"""return a representation of this step suitable for test"""
- clauses = remove_clauses(self.union, self.keepgroup)
- try:
- inputmap = varmap_test_repr(self.inputmap, self.plan.tablesinorder)
- outputmap = varmap_test_repr(self.outputmap, self.plan.tablesinorder)
- except AttributeError:
- inputmap = self.inputmap
- outputmap = self.outputmap
- try:
+ with remove_and_restore_clauses(self.union, self.keepgroup):
+ try:
+ inputmap = varmap_test_repr(self.inputmap, self.plan.tablesinorder)
+ outputmap = varmap_test_repr(self.outputmap, self.plan.tablesinorder)
+ except AttributeError:
+ inputmap = self.inputmap
+ outputmap = self.outputmap
return (self.__class__.__name__,
- sorted((r.as_string(kwargs=self.plan.args), r.solutions)
- for r in self.union.children),
- sorted(self.sources), inputmap, outputmap)
- finally:
- restore_clauses(self.union, self.keepgroup, clauses)
+ sorted((r.as_string(kwargs=self.plan.args), r.solutions)
+ for r in self.union.children),
+ sorted(self.sources), inputmap, outputmap)
class AggrStep(LimitOffsetMixIn, Step):
--- a/server/querier.py Mon May 16 16:24:00 2011 +0200
+++ b/server/querier.py Wed Jul 20 18:21:47 2011 +0200
@@ -556,6 +556,8 @@
def insert_relation_defs(self):
session = self.session
repo = session.repo
+ edited_entities = {}
+ relations = {}
for subj, rtype, obj in self.relation_defs():
# if a string is given into args instead of an int, we get it here
if isinstance(subj, basestring):
@@ -567,12 +569,21 @@
elif not isinstance(obj, (int, long)):
obj = obj.entity.eid
if repo.schema.rschema(rtype).inlined:
- entity = session.entity_from_eid(subj)
- edited = EditedEntity(entity)
+ if subj not in edited_entities:
+ entity = session.entity_from_eid(subj)
+ edited = EditedEntity(entity)
+ edited_entities[subj] = edited
+ else:
+ edited = edited_entities[subj]
edited.edited_attribute(rtype, obj)
- repo.glob_update_entity(session, edited)
else:
- repo.glob_add_relation(session, subj, rtype, obj)
+ if rtype in relations:
+ relations[rtype].append((subj, obj))
+ else:
+ relations[rtype] = [(subj, obj)]
+ repo.glob_add_relations(session, relations)
+ for edited in edited_entities.itervalues():
+ repo.glob_update_entity(session, edited)
class QuerierHelper(object):
--- a/server/repository.py Mon May 16 16:24:00 2011 +0200
+++ b/server/repository.py Wed Jul 20 18:21:47 2011 +0200
@@ -34,6 +34,7 @@
import sys
import threading
import Queue
+from warnings import warn
from itertools import chain
from os.path import join
from datetime import datetime
@@ -54,18 +55,23 @@
BadConnectionId, Unauthorized, ValidationError,
RepositoryError, UniqueTogetherError, typed_eid, onevent)
from cubicweb import cwvreg, schema, server
-from cubicweb.server import utils, hook, pool, querier, sources
+from cubicweb.server import ShuttingDown, utils, hook, pool, querier, sources
from cubicweb.server.session import Session, InternalSession, InternalManager, \
security_enabled
from cubicweb.server.ssplanner import EditedEntity
+NO_CACHE_RELATIONS = set( [('require_permission', 'object'),
+ ('owned_by', 'object'),
+ ('created_by', 'object'),
+ ('cw_source', 'object'),
+ ])
def prefill_entity_caches(entity, relations):
session = entity._cw
# prefill entity relation caches
for rschema in entity.e_schema.subject_relations():
rtype = str(rschema)
- if rtype in schema.VIRTUAL_RTYPES:
+ if rtype in schema.VIRTUAL_RTYPES or (rtype, 'subject') in NO_CACHE_RELATIONS:
continue
if rschema.final:
entity.cw_attr_cache.setdefault(rtype, None)
@@ -74,13 +80,9 @@
session.empty_rset())
for rschema in entity.e_schema.object_relations():
rtype = str(rschema)
- if rtype in schema.VIRTUAL_RTYPES:
+ if rtype in schema.VIRTUAL_RTYPES or (rtype, 'object') in NO_CACHE_RELATIONS:
continue
entity.cw_set_relation_cache(rtype, 'object', session.empty_rset())
- # set inlined relation cache before call to after_add_entity
- for attr, value in relations:
- session.update_rel_cache_add(entity.eid, attr, value)
- del_existing_rel_if_needed(session, entity.eid, attr, value)
def del_existing_rel_if_needed(session, eidfrom, rtype, eidto):
"""delete existing relation when adding a new one if card is 1 or ?
@@ -91,14 +93,11 @@
this kind of behaviour has to be done in the repository so we don't have
hooks order hazardness
"""
- # skip that for internal session or if integrity explicitly disabled
- #
- # XXX we should imo rely on the orm to first fetch existing entity if any
- # then delete it.
- if session.is_internal_session \
- or not session.is_hook_category_activated('activeintegrity'):
+ # skip that if integrity explicitly disabled
+ if not session.is_hook_category_activated('activeintegrity'):
return
- card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
+ rdef = session.rtype_eids_rdef(rtype, eidfrom, eidto)
+ card = rdef.cardinality
# one may be tented to check for neweids but this may cause more than one
# relation even with '1?' cardinality if thoses relations are added in the
# same transaction where the entity is being created. This never occurs from
@@ -110,7 +109,7 @@
# * inlined relations will be implicitly deleted for the subject entity
# * we don't want read permissions to be applied but we want delete
# permission to be checked
- if card[0] in '1?' and not session.repo.schema.rschema(rtype).inlined:
+ if card[0] in '1?':
with security_enabled(session, read=False):
session.execute('DELETE X %s Y WHERE X eid %%(x)s, '
'NOT Y eid %%(y)s' % rtype,
@@ -178,8 +177,8 @@
# information (eg dump/restore/...)
config._cubes = ()
# only load hooks and entity classes in the registry
- config.cube_appobject_path = set(('hooks', 'entities'))
- config.cubicweb_appobject_path = set(('hooks', 'entities'))
+ config.__class__.cube_appobject_path = set(('hooks', 'entities'))
+ config.__class__.cubicweb_appobject_path = set(('hooks', 'entities'))
self.set_schema(config.load_schema())
config['connections-pool-size'] = 1
# will be reinitialized later from cubes found in the database
@@ -232,6 +231,7 @@
self.sources_by_eid = {}
if self.config.quick_start \
or not 'CWSource' in self.schema: # # 3.10 migration
+ self.system_source.init_creating()
return
session = self.internal_session()
try:
@@ -742,7 +742,7 @@
args[0] = ex.entity
ex.args = tuple(args)
raise
- except:
+ except Exception:
# FIXME: check error to catch internal errors
self.exception('unexpected error while executing %s with %s', rqlstring, args)
raise
@@ -940,7 +940,7 @@
checkshuttingdown=True):
"""return the user associated to the given session identifier"""
if checkshuttingdown and self.shutting_down:
- raise Exception('Repository is shutting down')
+ raise ShuttingDown('Repository is shutting down')
try:
session = self._sessions[sessionid]
except KeyError:
@@ -1092,17 +1092,6 @@
hook.CleanupDeletedEidsCacheOp.get_instance(session).add_data(entity.eid)
self._delete_info(session, entity, sourceuri, extid, scleanup)
- def delete_info_multi(self, session, entities, sourceuri, extids, scleanup=None):
- """same as delete_info but accepts a list of entities and
- extids with the same etype and belonging to the same source
- """
- # mark eid as being deleted in session info and setup cache update
- # operation
- op = hook.CleanupDeletedEidsCacheOp.get_instance(session)
- for entity in entities:
- op.add_data(entity.eid)
- self._delete_info_multi(session, entities, sourceuri, extids, scleanup)
-
def _delete_info(self, session, entity, sourceuri, extid, scleanup=None):
"""delete system information on deletion of an entity:
* delete all remaining relations from/to this entity
@@ -1131,19 +1120,18 @@
try:
session.execute(rql, {'x': eid, 'seid': scleanup},
build_descr=False)
- except:
+ except Exception:
self.exception('error while cascading delete for entity %s '
'from %s. RQL: %s', entity, sourceuri, rql)
- self.system_source.delete_info(session, entity, sourceuri, extid)
+ self.system_source.delete_info_multi(session, [entity], sourceuri)
- def _delete_info_multi(self, session, entities, sourceuri, extids, scleanup=None):
+ def _delete_info_multi(self, session, entities, sourceuri, scleanup=None):
"""same as _delete_info but accepts a list of entities with
the same etype and belinging to the same source.
"""
pendingrtypes = session.transaction_data.get('pendingrtypes', ())
# delete remaining relations: if user can delete the entity, he can
# delete all its relations without security checking
- assert entities and len(entities) == len(extids)
with security_enabled(session, read=False, write=False):
eids = [_e.eid for _e in entities]
in_eids = ','.join((str(eid) for eid in eids))
@@ -1162,10 +1150,10 @@
rql += ', NOT (Y cw_source S, S eid %(seid)s)'
try:
session.execute(rql, {'seid': scleanup}, build_descr=False)
- except:
+ except Exception:
self.exception('error while cascading delete for entity %s '
'from %s. RQL: %s', entities, sourceuri, rql)
- self.system_source.delete_info_multi(session, entities, sourceuri, extids)
+ self.system_source.delete_info_multi(session, entities, sourceuri)
def locate_relation_source(self, session, subject, rtype, object):
subjsource = self.source_from_eid(subject, session)
@@ -1229,16 +1217,24 @@
if server.DEBUG & server.DBG_REPO:
print 'ADD entity', self, entity.__regid__, entity.eid, edited
relations = []
+ prefill_entity_caches(entity, relations)
if source.should_call_hooks:
self.hm.call_hooks('before_add_entity', session, entity=entity)
+ activintegrity = session.is_hook_category_activated('activeintegrity')
for attr in edited.iterkeys():
rschema = eschema.subjrels[attr]
if not rschema.final: # inlined relation
- relations.append((attr, edited[attr]))
+ value = edited[attr]
+ relations.append((attr, value))
+ session.update_rel_cache_add(entity.eid, attr, value)
+ rdef = session.rtype_eids_rdef(attr, entity.eid, value)
+ if rdef.cardinality[1] in '1?' and activintegrity:
+ with security_enabled(session, read=False):
+ session.execute('DELETE X %s Y WHERE Y eid %%(y)s' % attr,
+ {'x': entity.eid, 'y': value})
edited.set_defaults()
if session.is_hook_category_activated('integrity'):
edited.check(creation=True)
- prefill_entity_caches(entity, relations)
try:
source.add_entity(session, entity)
except UniqueTogetherError, exc:
@@ -1340,6 +1336,16 @@
def glob_delete_entities(self, session, eids):
"""delete a list of entities and all related entities from the repository"""
+ # mark eids as being deleted in session info and setup cache update
+ # operation (register pending eids before actual deletion to avoid
+ # multiple call to glob_delete_entities)
+ op = hook.CleanupDeletedEidsCacheOp.get_instance(session)
+ if not isinstance(eids, (set, frozenset)):
+ warn('[3.13] eids should be given as a set', DeprecationWarning,
+ stacklevel=2)
+ eids = frozenset(eids)
+ eids = eids - op._container
+ op._container |= eids
data_by_etype_source = {} # values are ([list of eids],
# [list of extid],
# [list of entities])
@@ -1351,43 +1357,95 @@
for eid in eids:
etype, sourceuri, extid = self.type_and_source_from_eid(eid, session)
+ # XXX should cache entity's cw_metainformation
entity = session.entity_from_eid(eid, etype)
- _key = (etype, sourceuri)
- if _key not in data_by_etype_source:
- data_by_etype_source[_key] = ([eid], [extid], [entity])
- else:
- _data = data_by_etype_source[_key]
- _data[0].append(eid)
- _data[1].append(extid)
- _data[2].append(entity)
- for (etype, sourceuri), (eids, extids, entities) in data_by_etype_source.iteritems():
+ try:
+ data_by_etype_source[(etype, sourceuri)].append(entity)
+ except KeyError:
+ data_by_etype_source[(etype, sourceuri)] = [entity]
+ for (etype, sourceuri), entities in data_by_etype_source.iteritems():
if server.DEBUG & server.DBG_REPO:
- print 'DELETE entities', etype, eids
- #print 'DELETE entities', etype, len(eids)
+ print 'DELETE entities', etype, [entity.eid for entity in entities]
source = self.sources_by_uri[sourceuri]
if source.should_call_hooks:
self.hm.call_hooks('before_delete_entity', session, entities=entities)
- self._delete_info_multi(session, entities, sourceuri, extids) # xxx
+ self._delete_info_multi(session, entities, sourceuri)
source.delete_entities(session, entities)
if source.should_call_hooks:
self.hm.call_hooks('after_delete_entity', session, entities=entities)
- # don't clear cache here this is done in a hook on commit
+ # don't clear cache here, it is done in a hook on commit
def glob_add_relation(self, session, subject, rtype, object):
"""add a relation to the repository"""
- if server.DEBUG & server.DBG_REPO:
- print 'ADD relation', subject, rtype, object
- source = self.locate_relation_source(session, subject, rtype, object)
- if source.should_call_hooks:
- del_existing_rel_if_needed(session, subject, rtype, object)
- self.hm.call_hooks('before_add_relation', session,
- eidfrom=subject, rtype=rtype, eidto=object)
- source.add_relation(session, subject, rtype, object)
- rschema = self.schema.rschema(rtype)
- session.update_rel_cache_add(subject, rtype, object, rschema.symmetric)
- if source.should_call_hooks:
- self.hm.call_hooks('after_add_relation', session,
- eidfrom=subject, rtype=rtype, eidto=object)
+ self.glob_add_relations(session, {rtype: [(subject, object)]})
+
+ def glob_add_relations(self, session, relations):
+ """add several relations to the repository
+
+ relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
+ """
+ sources = {}
+ subjects_by_types = {}
+ objects_by_types = {}
+ activintegrity = session.is_hook_category_activated('activeintegrity')
+ for rtype, eids_subj_obj in relations.iteritems():
+ if server.DEBUG & server.DBG_REPO:
+ for subjeid, objeid in relations:
+ print 'ADD relation', subjeid, rtype, objeid
+ for subjeid, objeid in eids_subj_obj:
+ source = self.locate_relation_source(session, subjeid, rtype, objeid)
+ if source not in sources:
+ relations_by_rtype = {}
+ sources[source] = relations_by_rtype
+ else:
+ relations_by_rtype = sources[source]
+ if rtype in relations_by_rtype:
+ relations_by_rtype[rtype].append((subjeid, objeid))
+ else:
+ relations_by_rtype[rtype] = [(subjeid, objeid)]
+ if not activintegrity:
+ continue
+ # take care to relation of cardinality '?1', as all eids will
+ # be inserted later, we've remove duplicated eids since they
+ # won't be catched by `del_existing_rel_if_needed`
+ rdef = session.rtype_eids_rdef(rtype, subjeid, objeid)
+ card = rdef.cardinality
+ if card[0] in '?1':
+ with security_enabled(session, read=False):
+ session.execute('DELETE X %s Y WHERE X eid %%(x)s, '
+ 'NOT Y eid %%(y)s' % rtype,
+ {'x': subjeid, 'y': objeid})
+ subjects = subjects_by_types.setdefault(rdef, {})
+ if subjeid in subjects:
+ del relations_by_rtype[rtype][subjects[subjeid]]
+ subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
+ continue
+ subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
+ if card[1] in '?1':
+ with security_enabled(session, read=False):
+ session.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
+ 'NOT X eid %%(x)s' % rtype,
+ {'x': subjeid, 'y': objeid})
+ objects = objects_by_types.setdefault(rdef, {})
+ if objeid in objects:
+ del relations_by_rtype[rtype][objects[objeid]]
+ objects[objeid] = len(relations_by_rtype[rtype])
+ continue
+ objects[objeid] = len(relations_by_rtype[rtype])
+ for source, relations_by_rtype in sources.iteritems():
+ if source.should_call_hooks:
+ for rtype, source_relations in relations_by_rtype.iteritems():
+ self.hm.call_hooks('before_add_relation', session,
+ rtype=rtype, eids_from_to=source_relations)
+ for rtype, source_relations in relations_by_rtype.iteritems():
+ source.add_relations(session, rtype, source_relations)
+ rschema = self.schema.rschema(rtype)
+ for subjeid, objeid in source_relations:
+ session.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
+ if source.should_call_hooks:
+ for rtype, source_relations in relations_by_rtype.iteritems():
+ self.hm.call_hooks('after_add_relation', session,
+ rtype=rtype, eids_from_to=source_relations)
def glob_delete_relation(self, session, subject, rtype, object):
"""delete a relation from the repository"""
--- a/server/rqlannotation.py Mon May 16 16:24:00 2011 +0200
+++ b/server/rqlannotation.py Wed Jul 20 18:21:47 2011 +0200
@@ -78,18 +78,19 @@
continue
lhs, rhs = rel.get_parts()
onlhs = ref is lhs
+ role = 'subject' if onlhs else 'object'
if rel.r_type == 'eid':
if not (onlhs and len(stinfo['relations']) > 1):
break
if not stinfo['constnode']:
- joins.add(rel)
+ joins.add( (rel, role) )
continue
elif rel.r_type == 'identity':
# identity can't be used as principal, so check other relation are used
# XXX explain rhs.operator == '='
if rhs.operator != '=' or len(stinfo['relations']) <= 1: #(stinfo['constnode'] and rhs.operator == '='):
break
- joins.add(rel)
+ joins.add( (rel, role) )
continue
rschema = getrschema(rel.r_type)
if rel.optional:
@@ -116,7 +117,7 @@
# need join anyway if the variable appears in a final or
# inlined relation
break
- joins.add(rel)
+ joins.add( (rel, role) )
continue
if not stinfo['constnode']:
if rschema.inlined and rel.neged(strict=True):
@@ -129,7 +130,7 @@
break
elif rschema.symmetric and stinfo['selected']:
break
- joins.add(rel)
+ joins.add( (rel, role) )
else:
# if there is at least one ambigous relation and no other to
# restrict types, can't be invariant since we need to filter out
@@ -169,10 +170,15 @@
diffscope_rels = {}
ored_rels = set()
diffscope_rels = set()
- for rel in _sort(relations):
+ for rel, role in _sort(relations):
# note: only eid and has_text among all final relations may be there
if rel.r_type in ('eid', 'identity'):
continue
+ if rel.optional is not None and len(relations) > 1:
+ if role == 'subject' and rel.optional == 'right':
+ continue
+ if role == 'object' and rel.optional == 'left':
+ continue
if rel.ored(traverse_scope=True):
ored_rels.add(rel)
elif rel.scope is scope:
@@ -265,9 +271,17 @@
return has_text_query
def is_ambiguous(self, var):
- # ignore has_text relation
- if len([rel for rel in var.stinfo['relations']
- if rel.scope is var.scope and rel.r_type == 'has_text']) == 1:
+ # ignore has_text relation when we know it will be used as principal.
+ # This is expected by the rql2sql generator which will use the `entities`
+ # table to filter out by type if necessary, This optimisation is very
+ # interesting in multi-sources cases, as it may avoid a costly query
+ # on sources to get all entities of a given type to achieve this, while
+ # we have all the necessary information.
+ root = var.stmt.root # Union node
+ # rel.scope -> Select or Exists node, so add .parent to get Union from
+ # Select node
+ rels = [rel for rel in var.stinfo['relations'] if rel.scope.parent is root]
+ if len(rels) == 1 and rels[0].r_type == 'has_text':
return False
try:
data = var.stmt._deamb_data
--- a/server/schemaserial.py Mon May 16 16:24:00 2011 +0200
+++ b/server/schemaserial.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -566,7 +566,7 @@
yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, '
'E mainvars %%(v)s, X %s_permission E WHERE X eid %%(x)s' % action,
{'e': unicode(rqlexpr.expression),
- 'v': unicode(rqlexpr.mainvars),
+ 'v': unicode(','.join(sorted(rqlexpr.mainvars))),
't': unicode(rqlexpr.__class__.__name__)})
# update functions
--- a/server/serverctl.py Mon May 16 16:24:00 2011 +0200
+++ b/server/serverctl.py Wed Jul 20 18:21:47 2011 +0200
@@ -355,7 +355,7 @@
print '-> user %s created.' % user
if dbname in helper.list_databases(cursor):
if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname):
- cursor.execute('DROP DATABASE %s' % dbname)
+ cursor.execute('DROP DATABASE "%s"' % dbname)
else:
print ('you may want to run "cubicweb-ctl db-init '
'--drop %s" manually to continue.' % config.appid)
@@ -560,6 +560,15 @@
"""
name = 'reset-admin-pwd'
arguments = '<instance>'
+ options = (
+ ('password',
+ {'short': 'p', 'type' : 'string', 'metavar' : '<new-password>',
+ 'default' : None,
+ 'help': 'Use this password instead of prompt for one.\n'
+ '/!\ THIS IS AN INSECURE PRACTICE /!\ \n'
+ 'the password will appear in shell history'}
+ ),
+ )
def run(self, args):
"""run the command with its specific arguments"""
@@ -586,15 +595,18 @@
print " fix your sources file before running this command"
cnx.close()
sys.exit(1)
- # ask for a new password
- _, passwd = manager_userpasswd(adminlogin, confirm=True,
- passwdmsg='new password for %s' % adminlogin)
+ if self.config.password is None:
+ # ask for a new password
+ msg = 'new password for %s' % adminlogin
+ _, pwd = manager_userpasswd(adminlogin, confirm=True, passwdmsg=msg)
+ else:
+ pwd = self.config.password
try:
cursor.execute("UPDATE cw_CWUser SET cw_upassword=%(p)s WHERE cw_login=%(l)s",
- {'p': dbhelper.binary_value(crypt_password(passwd)), 'l': adminlogin})
+ {'p': dbhelper.binary_value(crypt_password(pwd)), 'l': adminlogin})
sconfig = Configuration(options=USER_OPTIONS)
sconfig['login'] = adminlogin
- sconfig['password'] = passwd
+ sconfig['password'] = pwd
sourcescfg['admin'] = sconfig
config.write_sources_file(sourcescfg)
except Exception, ex:
@@ -691,19 +703,20 @@
'Continue anyway?' % filename):
raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename)
-def _local_dump(appid, output):
+
+def _local_dump(appid, output, format='native'):
config = ServerConfiguration.config_for(appid)
config.quick_start = True
mih = config.migration_handler(connect=False, verbosity=1)
- mih.backup_database(output, askconfirm=False)
+ mih.backup_database(output, askconfirm=False, format=format)
mih.shutdown()
-def _local_restore(appid, backupfile, drop, systemonly=True):
+def _local_restore(appid, backupfile, drop, systemonly=True, format='native'):
config = ServerConfiguration.config_for(appid)
config.verbosity = 1 # else we won't be asked for confirmation on problems
config.quick_start = True
mih = config.migration_handler(connect=False, verbosity=1)
- mih.restore_database(backupfile, drop, systemonly, askconfirm=False)
+ mih.restore_database(backupfile, drop, systemonly, askconfirm=False, format=format)
repo = mih.repo_connect()
# version of the database
dbversions = repo.get_versions()
@@ -777,6 +790,12 @@
'default' : False,
'help': 'Use sudo on the remote host.'}
),
+ ('format',
+ {'short': 'f', 'default': 'native', 'type': 'choice',
+ 'choices': ('native', 'portable'),
+ 'help': '"native" format uses db backend utilities to dump the database. '
+ '"portable" format uses a database independent format'}
+ ),
)
def run(self, args):
@@ -785,7 +804,9 @@
host, appid = appid.split(':')
_remote_dump(host, appid, self.config.output, self.config.sudo)
else:
- _local_dump(appid, self.config.output)
+ _local_dump(appid, self.config.output, format=self.config.format)
+
+
class DBRestoreCommand(Command):
@@ -811,13 +832,33 @@
'instance data. In that case, <backupfile> is expected to be the '
'timestamp of the backup to restore, not a file'}
),
+ ('format',
+ {'short': 'f', 'default': 'native', 'type': 'choice',
+ 'choices': ('native', 'portable'),
+ 'help': 'the format used when dumping the database'}),
)
def run(self, args):
appid, backupfile = args
+ if self.config.format == 'portable':
+ # we need to ensure a DB exist before restoring from portable format
+ if not self.config.no_drop:
+ try:
+ CWCTL.run(['db-create', '--automatic', appid])
+ except SystemExit, exc:
+ # continue if the command exited with status 0 (success)
+ if exc.code:
+ raise
_local_restore(appid, backupfile,
drop=not self.config.no_drop,
- systemonly=not self.config.restore_all)
+ systemonly=not self.config.restore_all,
+ format=self.config.format)
+ if self.config.format == 'portable':
+ try:
+ CWCTL.run(['db-rebuild-fti', appid])
+ except SystemExit, exc:
+ if exc.code:
+ raise
class DBCopyCommand(Command):
@@ -850,6 +891,12 @@
'default' : False,
'help': 'Use sudo on the remote host.'}
),
+ ('format',
+ {'short': 'f', 'default': 'native', 'type': 'choice',
+ 'choices': ('native', 'portable'),
+ 'help': '"native" format uses db backend utilities to dump the database. '
+ '"portable" format uses a database independent format'}
+ ),
)
def run(self, args):
@@ -861,8 +908,9 @@
host, srcappid = srcappid.split(':')
_remote_dump(host, srcappid, output, self.config.sudo)
else:
- _local_dump(srcappid, output)
- _local_restore(destappid, output, not self.config.no_drop)
+ _local_dump(srcappid, output, format=self.config.format)
+ _local_restore(destappid, output, not self.config.no_drop,
+ self.config.format)
if self.config.keep_dump:
print '-> you can get the dump file at', output
else:
--- a/server/session.py Mon May 16 16:24:00 2011 +0200
+++ b/server/session.py Wed Jul 20 18:21:47 2011 +0200
@@ -33,12 +33,15 @@
from yams import BASE_TYPES
from cubicweb import Binary, UnknownEid, QueryError, schema
+from cubicweb.selectors import objectify_selector
from cubicweb.req import RequestSessionBase
from cubicweb.dbapi import ConnectionProperties
from cubicweb.utils import make_uid, RepeatList
from cubicweb.rqlrewrite import RQLRewriter
+from cubicweb.server import ShuttingDown
from cubicweb.server.edition import EditedEntity
+
ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
NO_UNDO_TYPES = schema.SCHEMA_TYPES.copy()
@@ -58,6 +61,46 @@
description.append(term.get_type(solution, args))
return description
+@objectify_selector
+def is_user_session(cls, req, **kwargs):
+ """repository side only selector returning 1 if the session is a regular
+ user session and not an internal session
+ """
+ return not req.is_internal_session
+
+@objectify_selector
+def is_internal_session(cls, req, **kwargs):
+ """repository side only selector returning 1 if the session is not a regular
+ user session but an internal session
+ """
+ return req.is_internal_session
+
+@objectify_selector
+def repairing(cls, req, **kwargs):
+ """repository side only selector returning 1 if the session is not a regular
+ user session but an internal session
+ """
+ return req.vreg.config.repairing
+
+
+class transaction(object):
+ """context manager to enter a transaction for a session: when exiting the
+ `with` block on exception, call `session.rollback()`, else call
+ `session.commit()` on normal exit
+ """
+ def __init__(self, session, free_cnxset=True):
+ self.session = session
+ self.free_cnxset = free_cnxset
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exctype, exc, traceback):
+ if exctype:
+ self.session.rollback(free_cnxset=self.free_cnxset)
+ else:
+ self.session.commit(free_cnxset=self.free_cnxset)
+
class hooks_control(object):
"""context manager to control activated hooks categories.
@@ -166,11 +209,22 @@
self.__threaddata = threading.local()
self._threads_in_transaction = set()
self._closed = False
+ self._closed_lock = threading.Lock()
def __unicode__(self):
return '<%ssession %s (%s 0x%x)>' % (
self.cnxtype, unicode(self.user.login), self.id, id(self))
+ def transaction(self, free_cnxset=True):
+ """return context manager to enter a transaction for the session: when
+ exiting the `with` block on exception, call `session.rollback()`, else
+ call `session.commit()` on normal exit.
+
+ The `free_cnxset` will be given to rollback/commit methods to indicate
+ wether the connections set should be freed or not.
+ """
+ return transaction(self, free_cnxset)
+
def set_tx_data(self, txid=None):
if txid is None:
txid = threading.currentThread().getName()
@@ -213,14 +267,34 @@
You may use this in hooks when you know both eids of the relation you
want to add.
"""
+ self.add_relations([(rtype, [(fromeid, toeid)])])
+
+ def add_relations(self, relations):
+ '''set many relation using a shortcut similar to the one in add_relation
+
+ relations is a list of 2-uples, the first element of each
+ 2-uple is the rtype, and the second is a list of (fromeid,
+ toeid) tuples
+ '''
+ edited_entities = {}
+ relations_dict = {}
with security_enabled(self, False, False):
- if self.vreg.schema[rtype].inlined:
- entity = self.entity_from_eid(fromeid)
- edited = EditedEntity(entity)
- edited.edited_attribute(rtype, toeid)
+ for rtype, eids in relations:
+ if self.vreg.schema[rtype].inlined:
+ for fromeid, toeid in eids:
+ if fromeid not in edited_entities:
+ entity = self.entity_from_eid(fromeid)
+ edited = EditedEntity(entity)
+ edited_entities[fromeid] = edited
+ else:
+ edited = edited_entities[fromeid]
+ edited.edited_attribute(rtype, toeid)
+ else:
+ relations_dict[rtype] = eids
+ self.repo.glob_add_relations(self, relations_dict)
+ for edited in edited_entities.itervalues():
self.repo.glob_update_entity(self, edited)
- else:
- self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+
def delete_relation(self, fromeid, rtype, toeid):
"""provide direct access to the repository method to delete a relation.
@@ -358,17 +432,20 @@
"""
return eid in self.transaction_data.get('neweids', ())
- def schema_rproperty(self, rtype, eidfrom, eidto, rprop):
- rschema = self.repo.schema[rtype]
- subjtype = self.describe(eidfrom)[0]
- objtype = self.describe(eidto)[0]
- rdef = rschema.rdef(subjtype, objtype)
- return rdef.get(rprop)
+ def rtype_eids_rdef(self, rtype, eidfrom, eidto):
+ # use type_and_source_from_eid instead of type_from_eid for optimization
+ # (avoid two extra methods call)
+ subjtype = self.repo.type_and_source_from_eid(eidfrom, self)[0]
+ objtype = self.repo.type_and_source_from_eid(eidto, self)[0]
+ return self.vreg.schema.rschema(rtype).rdefs[(subjtype, objtype)]
# security control #########################################################
DEFAULT_SECURITY = object() # evaluated to true by design
+ def security_enabled(self, read=False, write=False):
+ return security_enabled(self, read=read, write=write)
+
@property
def read_security(self):
"""return a boolean telling if read security is activated or not"""
@@ -454,6 +531,11 @@
HOOKS_ALLOW_ALL = object()
HOOKS_DENY_ALL = object()
+ def allow_all_hooks_but(self, *categories):
+ return hooks_control(self, self.HOOKS_ALLOW_ALL, *categories)
+ def deny_all_hooks_but(self, *categories):
+ return hooks_control(self, self.HOOKS_DENY_ALL, *categories)
+
@property
def hooks_mode(self):
return getattr(self._threaddata, 'hooks_mode', self.HOOKS_ALLOW_ALL)
@@ -582,21 +664,22 @@
def set_pool(self):
"""the session need a pool to execute some queries"""
- if self._closed:
- self.reset_pool(True)
- raise Exception('try to set pool on a closed session')
- if self.pool is None:
- # get pool first to avoid race-condition
- self._threaddata.pool = pool = self.repo._get_pool()
- try:
- pool.pool_set()
- except:
- self._threaddata.pool = None
- self.repo._free_pool(pool)
- raise
- self._threads_in_transaction.add(
- (threading.currentThread(), pool) )
- return self._threaddata.pool
+ with self._closed_lock:
+ if self._closed:
+ self.reset_pool(True)
+ raise Exception('try to set pool on a closed session')
+ if self.pool is None:
+ # get pool first to avoid race-condition
+ self._threaddata.pool = pool = self.repo._get_pool()
+ try:
+ pool.pool_set()
+ except:
+ self._threaddata.pool = None
+ self.repo._free_pool(pool)
+ raise
+ self._threads_in_transaction.add(
+ (threading.currentThread(), pool) )
+ return self._threaddata.pool
def _free_thread_pool(self, thread, pool, force_close=False):
try:
@@ -834,7 +917,8 @@
def close(self):
"""do not close pool on session close, since they are shared now"""
- self._closed = True
+ with self._closed_lock:
+ self._closed = True
# copy since _threads_in_transaction maybe modified while waiting
for thread, pool in self._threads_in_transaction.copy():
if thread is threading.currentThread():
@@ -987,6 +1071,11 @@
# deprecated ###############################################################
+ @deprecated('[3.13] use getattr(session.rtype_eids_rdef(rtype, eidfrom, eidto), prop)')
+ def schema_rproperty(self, rtype, eidfrom, eidto, rprop):
+ return getattr(self.rtype_eids_rdef(rtype, eidfrom, eidto), rprop)
+
+
@deprecated("[3.7] execute is now unsafe by default in hooks/operation. You"
" can also control security with the security_enabled context "
"manager")
@@ -1056,7 +1145,7 @@
"""connections pool, set according to transaction mode for each query"""
if self.repo.shutting_down:
self.reset_pool(True)
- raise Exception('repository is shutting down')
+ raise ShuttingDown('repository is shutting down')
return getattr(self._threaddata, 'pool', None)
--- a/server/sources/__init__.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/__init__.py Wed Jul 20 18:21:47 2011 +0200
@@ -139,11 +139,11 @@
return -1
return cmp(self.uri, other.uri)
- def backup(self, backupfile, confirm):
+ def backup(self, backupfile, confirm, format='native'):
"""method called to create a backup of source's data"""
pass
- def restore(self, backupfile, confirm, drop):
+ def restore(self, backupfile, confirm, drop, format='native'):
"""method called to restore a backup of source's data"""
pass
@@ -434,6 +434,13 @@
"""add a relation to the source"""
raise NotImplementedError()
+ def add_relations(self, session, rtype, subj_obj_list):
+ """add a relations to the source"""
+ # override in derived classes if you feel you can
+ # optimize
+ for subject, object in subj_obj_list:
+ self.add_relation(session, subject, rtype, object)
+
def delete_relation(self, session, subject, rtype, object):
"""delete a relation from the source"""
raise NotImplementedError()
@@ -455,19 +462,12 @@
"""mark entity as being modified, fulltext reindex if needed"""
raise NotImplementedError()
- def delete_info(self, session, entity, uri, extid):
- """delete system information on deletion of an entity by transfering
- record from the entities table to the deleted_entities table
+ def delete_info_multi(self, session, entities, uri):
+ """delete system information on deletion of a list of entities with the
+ same etype and belinging to the same source
"""
raise NotImplementedError()
- def delete_info_multi(self, session, entities, uri, extids):
- """ame as delete_info but accepts a list of entities with
- the same etype and belinging to the same source.
- """
- for entity, extid in itertools.izip(entities, extids):
- self.delete_info(session, entity, uri, extid)
-
def modified_entities(self, session, etypes, mtime):
"""return a 2-uple:
* list of (etype, eid) of entities of the given types which have been
--- a/server/sources/datafeed.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/datafeed.py Wed Jul 20 18:21:47 2011 +0200
@@ -57,7 +57,8 @@
)
def __init__(self, repo, source_config, eid=None):
AbstractSource.__init__(self, repo, source_config, eid)
- self.update_config(None, self.check_conf_dict(eid, source_config))
+ self.update_config(None, self.check_conf_dict(eid, source_config,
+ fail_if_unknown=False))
def check_config(self, source_entity):
"""check configuration of source entity"""
@@ -118,9 +119,9 @@
def fresh(self):
if self.latest_retrieval is None:
return False
- return datetime.now() < (self.latest_retrieval + self.synchro_interval)
+ return datetime.utcnow() < (self.latest_retrieval + self.synchro_interval)
- def pull_data(self, session, force=False):
+ def pull_data(self, session, force=False, raise_on_error=False):
if not force and self.fresh():
return {}
if self.config['delete-entities']:
@@ -135,6 +136,8 @@
if parser.process(url):
error = True
except IOError, exc:
+ if raise_on_error:
+ raise
self.error('could not pull data while processing %s: %s',
url, exc)
error = True
@@ -148,7 +151,7 @@
for etype, eids in byetype.iteritems():
session.execute('DELETE %s X WHERE X eid IN (%s)'
% (etype, ','.join(eids)))
- self.latest_retrieval = datetime.now()
+ self.latest_retrieval = datetime.utcnow()
session.execute('SET X latest_retrieval %(date)s WHERE X eid %(x)s',
{'x': self.eid, 'date': self.latest_retrieval})
return parser.stats
--- a/server/sources/native.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/native.py Wed Jul 20 18:21:47 2011 +0200
@@ -28,21 +28,29 @@
__docformat__ = "restructuredtext en"
-from pickle import loads, dumps
+try:
+ from cPickle import loads, dumps
+ import cPickle as pickle
+except ImportError:
+ from pickle import loads, dumps
+ import pickle
from threading import Lock
from datetime import datetime
from base64 import b64decode, b64encode
from contextlib import contextmanager
-from os.path import abspath
+from os.path import abspath, basename
import re
import itertools
+import zipfile
+import logging
+import sys
from logilab.common.compat import any
from logilab.common.cache import Cache
from logilab.common.decorators import cached, clear_cache
from logilab.common.configuration import Method
from logilab.common.shellutils import getlogin
-from logilab.database import get_db_helper
+from logilab.database import get_db_helper, sqlgen
from yams import schema2sql as y2sql
from yams.schema import role_name
@@ -157,6 +165,15 @@
class UndoException(Exception):
"""something went wrong during undoing"""
+ def __unicode__(self):
+ """Called by the unicode builtin; should return a Unicode object
+
+ Type of UndoException message must be `unicode` by design in CubicWeb.
+
+ .. warning::
+ This method is not available in python2.5"""
+ assert isinstance(self.message, unicode)
+ return self.message
def _undo_check_relation_target(tentity, rdef, role):
"""check linked entity has not been redirected for this relation"""
@@ -354,24 +371,44 @@
_pool.pool_reset()
self.repo._free_pool(_pool)
- def backup(self, backupfile, confirm):
+ def backup(self, backupfile, confirm, format='native'):
"""method called to create a backup of the source's data"""
- self.close_pool_connections()
- try:
- self.backup_to_file(backupfile, confirm)
- finally:
- self.open_pool_connections()
+ if format == 'portable':
+ self.repo.fill_schema()
+ self.set_schema(self.repo.schema)
+ helper = DatabaseIndependentBackupRestore(self)
+ self.close_pool_connections()
+ try:
+ helper.backup(backupfile)
+ finally:
+ self.open_pool_connections()
+ elif format == 'native':
+ self.close_pool_connections()
+ try:
+ self.backup_to_file(backupfile, confirm)
+ finally:
+ self.open_pool_connections()
+ else:
+ raise ValueError('Unknown format %r' % format)
- def restore(self, backupfile, confirm, drop):
+
+ def restore(self, backupfile, confirm, drop, format='native'):
"""method called to restore a backup of source's data"""
if self.repo.config.open_connections_pools:
self.close_pool_connections()
try:
- self.restore_from_file(backupfile, confirm, drop=drop)
+ if format == 'portable':
+ helper = DatabaseIndependentBackupRestore(self)
+ helper.restore(backupfile)
+ elif format == 'native':
+ self.restore_from_file(backupfile, confirm, drop=drop)
+ else:
+ raise ValueError('Unknown format %r' % format)
finally:
if self.repo.config.open_connections_pools:
self.open_pool_connections()
+
def init(self, activated, source_entity):
self.init_creating(source_entity._cw.pool)
@@ -628,22 +665,42 @@
def add_relation(self, session, subject, rtype, object, inlined=False):
"""add a relation to the source"""
- self._add_relation(session, subject, rtype, object, inlined)
+ self._add_relations(session, rtype, [(subject, object)], inlined)
if session.undoable_action('A', rtype):
self._record_tx_action(session, 'tx_relation_actions', 'A',
eid_from=subject, rtype=rtype, eid_to=object)
- def _add_relation(self, session, subject, rtype, object, inlined=False):
+ def add_relations(self, session, rtype, subj_obj_list, inlined=False):
+ """add a relations to the source"""
+ self._add_relations(session, rtype, subj_obj_list, inlined)
+ if session.undoable_action('A', rtype):
+ for subject, object in subj_obj_list:
+ self._record_tx_action(session, 'tx_relation_actions', 'A',
+ eid_from=subject, rtype=rtype, eid_to=object)
+
+ def _add_relations(self, session, rtype, subj_obj_list, inlined=False):
"""add a relation to the source"""
+ sql = []
if inlined is False:
- attrs = {'eid_from': subject, 'eid_to': object}
- sql = self.sqlgen.insert('%s_relation' % rtype, attrs)
+ attrs = [{'eid_from': subject, 'eid_to': object}
+ for subject, object in subj_obj_list]
+ sql.append((self.sqlgen.insert('%s_relation' % rtype, attrs[0]), attrs))
else: # used by data import
- etype = session.describe(subject)[0]
- attrs = {'cw_eid': subject, SQL_PREFIX + rtype: object}
- sql = self.sqlgen.update(SQL_PREFIX + etype, attrs,
- ['cw_eid'])
- self.doexec(session, sql, attrs)
+ etypes = {}
+ for subject, object in subj_obj_list:
+ etype = session.describe(subject)[0]
+ if etype in etypes:
+ etypes[etype].append((subject, object))
+ else:
+ etypes[etype] = [(subject, object)]
+ for subj_etype, subj_obj_list in etypes.iteritems():
+ attrs = [{'cw_eid': subject, SQL_PREFIX + rtype: object}
+ for subject, object in subj_obj_list]
+ sql.append((self.sqlgen.update(SQL_PREFIX + etype, attrs[0],
+ ['cw_eid']),
+ attrs))
+ for statement, attrs in sql:
+ self.doexecmany(session, statement, attrs)
def delete_relation(self, session, subject, rtype, object):
"""delete a relation from the source"""
@@ -690,7 +747,7 @@
session.pool.connection(self.uri).rollback()
if self.repo.config.mode != 'test':
self.critical('transaction has been rollbacked')
- except:
+ except Exception, ex:
pass
if ex.__class__.__name__ == 'IntegrityError':
# need string comparison because of various backends
@@ -922,31 +979,13 @@
attrs = {'eid': entity.eid, 'mtime': datetime.now()}
self.doexec(session, self.sqlgen.update('entities', attrs, ['eid']), attrs)
- def delete_info(self, session, entity, uri, extid):
- """delete system information on deletion of an entity:
+ def delete_info_multi(self, session, entities, uri):
+ """delete system information on deletion of a list of entities with the
+ same etype and belinging to the same source
+
* update the fti
- * remove record from the entities table
- * transfer it to the deleted_entities table if the entity's type is
- multi-sources
- """
- self.fti_unindex_entities(session, [entity])
- attrs = {'eid': entity.eid}
- self.doexec(session, self.sqlgen.delete('entities', attrs), attrs)
- if not entity.__regid__ in self.multisources_etypes:
- return
- if extid is not None:
- assert isinstance(extid, str), type(extid)
- extid = b64encode(extid)
- attrs = {'type': entity.__regid__, 'eid': entity.eid, 'extid': extid,
- 'source': uri, 'dtime': datetime.now()}
- self.doexec(session, self.sqlgen.insert('deleted_entities', attrs), attrs)
-
- def delete_info_multi(self, session, entities, uri, extids):
- """delete system information on deletion of an entity:
- * update the fti
- * remove record from the entities table
- * transfer it to the deleted_entities table if the entity's type is
- multi-sources
+ * remove record from the `entities` table
+ * transfer it to the `deleted_entities`
"""
self.fti_unindex_entities(session, entities)
attrs = {'eid': '(%s)' % ','.join([str(_e.eid) for _e in entities])}
@@ -955,7 +994,8 @@
return
attrs = {'type': entities[0].__regid__,
'source': uri, 'dtime': datetime.now()}
- for entity, extid in itertools.izip(entities, extids):
+ for entity in entities:
+ extid = entity.cw_metainformation()['extid']
if extid is not None:
assert isinstance(extid, str), type(extid)
extid = b64encode(extid)
@@ -1232,7 +1272,7 @@
self.repo.hm.call_hooks('before_add_relation', session,
eidfrom=subj, rtype=rtype, eidto=obj)
# add relation in the database
- self._add_relation(session, subj, rtype, obj, rdef.rtype.inlined)
+ self._add_relations(session, rtype, [(subj, obj)], rdef.rtype.inlined)
# set related cache
session.update_rel_cache_add(subj, rtype, obj, rdef.rtype.symmetric)
self.repo.hm.call_hooks('after_add_relation', session,
@@ -1272,7 +1312,7 @@
sql = self.sqlgen.delete(SQL_PREFIX + entity.__regid__, attrs)
self.doexec(session, sql, attrs)
# remove record from entities (will update fti if needed)
- self.delete_info(session, entity, self.uri, None)
+ self.delete_info_multi(session, [entity], self.uri)
self.repo.hm.call_hooks('after_delete_entity', session, entity=entity)
return ()
@@ -1544,3 +1584,218 @@
login = rset.rows[0][0]
authinfo['email_auth'] = True
return self.source.repo.check_auth_info(session, login, authinfo)
+
+class DatabaseIndependentBackupRestore(object):
+ """Helper class to perform db backend agnostic backup and restore
+
+ The backup and restore methods are used to dump / restore the
+ system database in a database independent format. The file is a
+ Zip archive containing the following files:
+
+ * format.txt: the format of the archive. Currently '1.0'
+ * tables.txt: list of filenames in the archive tables/ directory
+ * sequences.txt: list of filenames in the archive sequences/ directory
+ * versions.txt: the list of cube versions from CWProperty
+ * tables/<tablename>.<chunkno>: pickled data
+ * sequences/<sequencename>: pickled data
+
+ The pickled data format for tables and sequences is a tuple of 3 elements:
+ * the table name
+ * a tuple of column names
+ * a list of rows (as tuples with one element per column)
+
+ Tables are saved in chunks in different files in order to prevent
+ a too high memory consumption.
+ """
+ def __init__(self, source):
+ """
+ :param: source an instance of the system source
+ """
+ self._source = source
+ self.logger = logging.getLogger('cubicweb.ctl')
+ self.logger.setLevel(logging.INFO)
+ self.logger.addHandler(logging.StreamHandler(sys.stdout))
+ self.schema = self._source.schema
+ self.dbhelper = self._source.dbhelper
+ self.cnx = None
+ self.cursor = None
+ self.sql_generator = sqlgen.SQLGenerator()
+
+ def get_connection(self):
+ return self._source.get_connection()
+
+ def backup(self, backupfile):
+ archive=zipfile.ZipFile(backupfile, 'w')
+ self.cnx = self.get_connection()
+ try:
+ self.cursor = self.cnx.cursor()
+ self.cursor.arraysize=100
+ self.logger.info('writing metadata')
+ self.write_metadata(archive)
+ for seq in self.get_sequences():
+ self.logger.info('processing sequence %s', seq)
+ self.write_sequence(archive, seq)
+ for table in self.get_tables():
+ self.logger.info('processing table %s', table)
+ self.write_table(archive, table)
+ finally:
+ archive.close()
+ self.cnx.close()
+ self.logger.info('done')
+
+ def get_tables(self):
+ non_entity_tables = ['entities',
+ 'deleted_entities',
+ 'transactions',
+ 'tx_entity_actions',
+ 'tx_relation_actions',
+ ]
+ etype_tables = []
+ relation_tables = []
+ prefix = 'cw_'
+ for etype in self.schema.entities():
+ eschema = self.schema.eschema(etype)
+ print etype, eschema.final
+ if eschema.final:
+ continue
+ etype_tables.append('%s%s'%(prefix, etype))
+ for rtype in self.schema.relations():
+ rschema = self.schema.rschema(rtype)
+ if rschema.final or rschema.inlined:
+ continue
+ relation_tables.append('%s_relation' % rtype)
+ return non_entity_tables + etype_tables + relation_tables
+
+ def get_sequences(self):
+ return ['entities_id_seq']
+
+ def write_metadata(self, archive):
+ archive.writestr('format.txt', '1.0')
+ archive.writestr('tables.txt', '\n'.join(self.get_tables()))
+ archive.writestr('sequences.txt', '\n'.join(self.get_sequences()))
+ versions = self._get_versions()
+ versions_str = '\n'.join('%s %s' % (k,v)
+ for k,v in versions)
+ archive.writestr('versions.txt', versions_str)
+
+ def write_sequence(self, archive, seq):
+ sql = self.dbhelper.sql_sequence_current_state(seq)
+ columns, rows_iterator = self._get_cols_and_rows(sql)
+ rows = list(rows_iterator)
+ serialized = self._serialize(seq, columns, rows)
+ archive.writestr('sequences/%s' % seq, serialized)
+
+ def write_table(self, archive, table):
+ sql = 'SELECT * FROM %s' % table
+ columns, rows_iterator = self._get_cols_and_rows(sql)
+ self.logger.info('number of rows: %d', self.cursor.rowcount)
+ if table.startswith('cw_'): # entities
+ blocksize = 2000
+ else: # relations and metadata
+ blocksize = 10000
+ if self.cursor.rowcount > 0:
+ for i, start in enumerate(xrange(0, self.cursor.rowcount, blocksize)):
+ rows = list(itertools.islice(rows_iterator, blocksize))
+ serialized = self._serialize(table, columns, rows)
+ archive.writestr('tables/%s.%04d' % (table, i), serialized)
+ self.logger.debug('wrote rows %d to %d (out of %d) to %s.%04d',
+ start, start+len(rows)-1,
+ self.cursor.rowcount,
+ table, i)
+ else:
+ rows = []
+ serialized = self._serialize(table, columns, rows)
+ archive.writestr('tables/%s.%04d' % (table, 0), serialized)
+
+ def _get_cols_and_rows(self, sql):
+ process_result = self._source.iter_process_result
+ self.cursor.execute(sql)
+ columns = (d[0] for d in self.cursor.description)
+ rows = process_result(self.cursor)
+ return tuple(columns), rows
+
+ def _serialize(self, name, columns, rows):
+ return dumps((name, columns, rows), pickle.HIGHEST_PROTOCOL)
+
+ def restore(self, backupfile):
+ archive = zipfile.ZipFile(backupfile, 'r')
+ self.cnx = self.get_connection()
+ self.cursor = self.cnx.cursor()
+ sequences, tables, table_chunks = self.read_metadata(archive, backupfile)
+ for seq in sequences:
+ self.logger.info('restoring sequence %s', seq)
+ self.read_sequence(archive, seq)
+ for table in tables:
+ self.logger.info('restoring table %s', table)
+ self.read_table(archive, table, sorted(table_chunks[table]))
+ self.cnx.close()
+ archive.close()
+ self.logger.info('done')
+
+ def read_metadata(self, archive, backupfile):
+ formatinfo = archive.read('format.txt')
+ self.logger.info('checking metadata')
+ if formatinfo.strip() != "1.0":
+ self.logger.critical('Unsupported format in archive: %s', formatinfo)
+ raise ValueError('Unknown format in %s: %s' % (backupfile, formatinfo))
+ tables = archive.read('tables.txt').splitlines()
+ sequences = archive.read('sequences.txt').splitlines()
+ file_versions = self._parse_versions(archive.read('versions.txt'))
+ versions = set(self._get_versions())
+ if file_versions != versions:
+ self.logger.critical('Unable to restore : versions do not match')
+ self.logger.critical('Expected:\n%s', '\n'.join(list(sorted(versions))))
+ self.logger.critical('Found:\n%s', '\n'.join(list(sorted(file_versions))))
+ raise ValueError('Unable to restore : versions do not match')
+ table_chunks = {}
+ for name in archive.namelist():
+ if not name.startswith('tables/'):
+ continue
+ filename = basename(name)
+ tablename, _ext = filename.rsplit('.', 1)
+ table_chunks.setdefault(tablename, []).append(name)
+ return sequences, tables, table_chunks
+
+ def read_sequence(self, archive, seq):
+ seqname, columns, rows = loads(archive.read('sequences/%s' % seq))
+ assert seqname == seq
+ assert len(rows) == 1
+ assert len(rows[0]) == 1
+ value = rows[0][0]
+ sql = self.dbhelper.sql_restart_sequence(seq, value)
+ self.cursor.execute(sql)
+ self.cnx.commit()
+
+ def read_table(self, archive, table, filenames):
+ merge_args = self._source.merge_args
+ self.cursor.execute('DELETE FROM %s' % table)
+ self.cnx.commit()
+ row_count = 0
+ for filename in filenames:
+ tablename, columns, rows = loads(archive.read(filename))
+ assert tablename == table
+ if not rows:
+ continue
+ insert = self.sql_generator.insert(table,
+ dict(zip(columns, rows[0])))
+ for row in rows:
+ self.cursor.execute(insert, merge_args(dict(zip(columns, row)), {}))
+ row_count += len(rows)
+ self.cnx.commit()
+ self.logger.info('inserted %d rows', row_count)
+
+
+ def _parse_versions(self, version_str):
+ versions = set()
+ for line in version_str.splitlines():
+ versions.add(tuple(line.split()))
+ return versions
+
+ def _get_versions(self):
+ version_sql = 'SELECT cw_pkey, cw_value FROM cw_CWProperty'
+ versions = []
+ self.cursor.execute(version_sql)
+ for pkey, value in self.cursor.fetchall():
+ if pkey.startswith(u'system.version'):
+ versions.append((pkey, value))
+ return versions
--- a/server/sources/pyrorql.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/pyrorql.py Wed Jul 20 18:21:47 2011 +0200
@@ -234,6 +234,7 @@
etype, dexturi, dextid = cnx.describe(extid)
if dexturi == 'system' or not (
dexturi in self.repo.sources_by_uri or self._skip_externals):
+ assert etype in self.support_entities, etype
return self.repo.extid2eid(self, str(extid), etype, session), True
if dexturi in self.repo.sources_by_uri:
source = self.repo.sources_by_uri[dexturi]
@@ -312,9 +313,8 @@
def get_connection(self):
try:
return self._get_connection()
- except (ConnectionError, PyroError):
- self.critical("can't get connection to source %s", self.uri,
- exc_info=1)
+ except (ConnectionError, PyroError), ex:
+ self.critical("can't get connection to source %s: %s", self.uri, ex)
return ConnectionWrapper()
def check_connection(self, cnx):
--- a/server/sources/rql2sql.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/rql2sql.py Wed Jul 20 18:21:47 2011 +0200
@@ -50,7 +50,9 @@
__docformat__ = "restructuredtext en"
import threading
+from datetime import datetime, time
+from logilab.common.date import utcdatetime, utctime
from logilab.database import FunctionDescr, SQL_FUNCTIONS_REGISTRY
from rql import BadRQLQuery, CoercionError
@@ -70,7 +72,9 @@
stack.append(self.source_execute)
FunctionDescr.update_cb_stack = default_update_cb_stack
-LENGTH = SQL_FUNCTIONS_REGISTRY.get_function('LENGTH')
+get_func_descr = SQL_FUNCTIONS_REGISTRY.get_function
+
+LENGTH = get_func_descr('LENGTH')
def length_source_execute(source, session, value):
return len(value.getvalue())
LENGTH.source_execute = length_source_execute
@@ -82,6 +86,7 @@
newvar.prepare_annotation()
newvar.stinfo['scope'] = select
newvar._q_invariant = False
+ select.selection.append(VariableRef(newvar))
return newvar
def _fill_to_wrap_rel(var, newselect, towrap, schema):
@@ -91,10 +96,12 @@
towrap.add( (var, rel) )
for vref in rel.children[1].iget_nodes(VariableRef):
newivar = _new_var(newselect, vref.name)
- newselect.selection.append(VariableRef(newivar))
_fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
elif rschema.final:
towrap.add( (var, rel) )
+ for vref in rel.children[1].iget_nodes(VariableRef):
+ newivar = _new_var(newselect, vref.name)
+ newivar.stinfo['attrvar'] = (var, rel.r_type)
def rewrite_unstable_outer_join(select, solutions, unstable, schema):
"""if some optional variables are unstable, they should be selected in a
@@ -114,11 +121,6 @@
# extract aliases / selection
newvar = _new_var(newselect, var.name)
newselect.selection = [VariableRef(newvar)]
- for avar in select.defined_vars.itervalues():
- if avar.stinfo['attrvar'] is var:
- newavar = _new_var(newselect, avar.name)
- newavar.stinfo['attrvar'] = newvar
- newselect.selection.append(VariableRef(newavar))
towrap_rels = set()
_fill_to_wrap_rel(var, newselect, towrap_rels, schema)
# extract relations
@@ -245,47 +247,56 @@
table + '.eid_from')
return switchedsql.replace('__eid_from__', table + '.eid_to')
-def sort_term_selection(sorts, selectedidx, rqlst, groups):
+def sort_term_selection(sorts, rqlst, groups):
# XXX beurk
if isinstance(rqlst, list):
def append(term):
rqlst.append(term)
+ selectionidx = set(str(term) for term in rqlst)
else:
def append(term):
rqlst.selection.append(term.copy(rqlst))
+ selectionidx = set(str(term) for term in rqlst.selection)
+
for sortterm in sorts:
term = sortterm.term
- if not isinstance(term, Constant) and not str(term) in selectedidx:
- selectedidx.append(str(term))
+ if not isinstance(term, Constant) and not str(term) in selectionidx:
+ selectionidx.add(str(term))
append(term)
if groups:
for vref in term.iget_nodes(VariableRef):
if not vref in groups:
groups.append(vref)
-def fix_selection_and_group(rqlst, selectedidx, needwrap, selectsortterms,
+def fix_selection_and_group(rqlst, needwrap, selectsortterms,
sorts, groups, having):
if selectsortterms and sorts:
- sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups)
+ sort_term_selection(sorts, rqlst, not needwrap and groups)
+ groupvrefs = [vref for term in groups for vref in term.iget_nodes(VariableRef)]
if sorts and groups:
# when a query is grouped, ensure sort terms are grouped as well
for sortterm in sorts:
term = sortterm.term
- if not isinstance(term, Constant):
+ if not (isinstance(term, Constant) or \
+ (isinstance(term, Function) and
+ get_func_descr(term.name).aggregat)):
for vref in term.iget_nodes(VariableRef):
- if not vref in groups:
+ if not vref in groupvrefs:
groups.append(vref)
- if needwrap:
+ groupvrefs.append(vref)
+ if needwrap and (groups or having):
+ selectedidx = set(vref.name for term in rqlst.selection
+ for vref in term.get_nodes(VariableRef))
if groups:
- for vref in groups:
- if not vref.name in selectedidx:
- selectedidx.append(vref.name)
+ for vref in groupvrefs:
+ if vref.name not in selectedidx:
+ selectedidx.add(vref.name)
rqlst.selection.append(vref)
if having:
for term in having:
for vref in term.iget_nodes(VariableRef):
- if not vref.name in selectedidx:
- selectedidx.append(vref.name)
+ if vref.name not in selectedidx:
+ selectedidx.add(vref.name)
rqlst.selection.append(vref)
def iter_mapped_var_sels(stmt, variable):
@@ -308,12 +319,12 @@
break
if not isinstance(node, Function):
raise QueryError()
- func = SQL_FUNCTIONS_REGISTRY.get_function(node.name)
- if func.source_execute is None:
+ funcd = get_func_descr(node.name)
+ if funcd.source_execute is None:
raise QueryError('%s can not be called on mapped attribute'
% node.name)
state.source_cb_funcs.add(node)
- func.update_cb_stack(stack)
+ funcd.update_cb_stack(stack)
# IGenerator implementation for RQL->SQL #######################################
@@ -588,16 +599,16 @@
rconditions.append(condition)
else:
lconditions.append(condition)
- else:
- if louter is not None:
- raise BadRQLQuery()
+ elif louter is None:
# merge chains
self.outer_chains.remove(lchain)
+ rchain += lchain
self.mark_as_used_in_outer_join(leftalias)
- rchain += lchain
for alias, (aouter, aconditions, achain) in outer_tables.iteritems():
if achain is lchain:
outer_tables[alias] = (aouter, aconditions, rchain)
+ else:
+ raise BadRQLQuery()
# sql generation helpers ###################################################
@@ -802,23 +813,16 @@
# treat subqueries
self._subqueries_sql(select, state)
# generate sql for this select node
- selectidx = [str(term) for term in select.selection]
if needwrap:
outerselection = origselection[:]
if sorts and selectsortterms:
- outerselectidx = [str(term) for term in outerselection]
if distinct:
- sort_term_selection(sorts, outerselectidx,
- outerselection, groups)
- else:
- outerselectidx = selectidx[:]
- fix_selection_and_group(select, selectidx, needwrap,
- selectsortterms, sorts, groups, having)
+ sort_term_selection(sorts, outerselection, groups)
+ fix_selection_and_group(select, needwrap, selectsortterms,
+ sorts, groups, having)
if needwrap:
- fselectidx = outerselectidx
fneedwrap = len(outerselection) != len(origselection)
else:
- fselectidx = selectidx
fneedwrap = len(select.selection) != len(origselection)
if fneedwrap:
needalias = True
@@ -850,8 +854,12 @@
# sort
if sorts:
sqlsortterms = []
+ if needwrap:
+ selectidx = [str(term) for term in outerselection]
+ else:
+ selectidx = [str(term) for term in select.selection]
for sortterm in sorts:
- _term = self._sortterm_sql(sortterm, fselectidx)
+ _term = self._sortterm_sql(sortterm, selectidx)
if _term is not None:
sqlsortterms.append(_term)
if sqlsortterms:
@@ -1243,6 +1251,8 @@
return condition
self._state.add_outer_join_condition(leftalias, condition)
return
+ if leftalias is None:
+ leftalias = leftvar._q_sql.split('.', 1)[0]
self._state.replace_tables_by_outer_join(
leftalias, rightalias, outertype, '%s=%s' % (lhssql, rhs.accept(self)))
return ''
@@ -1254,11 +1264,16 @@
unification (eg X attr1 A, Y attr2 A). In case of selection,
nothing to do here.
"""
- for var in rhs_vars:
+ for vref in rhs_vars:
+ var = vref.variable
if var.name in self._varmap:
# ensure table is added
- self._var_info(var.variable)
- principal = var.variable.stinfo.get('principal')
+ self._var_info(var)
+ if isinstance(var, ColumnAlias):
+ # force sql generation whatever the computed principal
+ principal = 1
+ else:
+ principal = var.stinfo.get('principal')
if principal is not None and principal is not relation:
# we have to generate unification expression
lhssql = self._inlined_var_sql(relation.children[0].variable,
@@ -1424,6 +1439,14 @@
_id = value
if isinstance(_id, unicode):
_id = _id.encode()
+ # convert timestamp to utc.
+ # expect SET TiME ZONE to UTC at connection opening time.
+ # This shouldn't change anything for datetime without TZ.
+ value = self._args[_id]
+ if isinstance(value, datetime) and value.tzinfo is not None:
+ self._query_attrs[_id] = utcdatetime(value)
+ elif isinstance(value, time) and value.tzinfo is not None:
+ self._query_attrs[_id] = utctime(value)
else:
_id = str(id(constant)).replace('-', '', 1)
self._query_attrs[_id] = value
--- a/server/sources/storages.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sources/storages.py Wed Jul 20 18:21:47 2011 +0200
@@ -122,7 +122,7 @@
fpath = source.binary_to_str(value)
try:
return Binary(file(fpath, 'rb').read())
- except OSError, ex:
+ except EnvironmentError, ex:
source.critical("can't open %s: %s", value, ex)
return None
@@ -173,13 +173,15 @@
entity.cw_edited.edited_attribute(attr, Binary(fpath))
# Mark the old file as useless so the file will be removed at
# commit.
- DeleteFileOp.get_instance(entity._cw).add_data(oldpath)
+ if oldpath is not None:
+ DeleteFileOp.get_instance(entity._cw).add_data(oldpath)
return binary
def entity_deleted(self, entity, attr):
"""an entity using this storage for attr has been deleted"""
fpath = self.current_fs_path(entity, attr)
- DeleteFileOp.get_instance(entity._cw).add_data(fpath)
+ if fpath is not None:
+ DeleteFileOp.get_instance(entity._cw).add_data(fpath)
def new_fs_path(self, entity, attr):
# We try to get some hint about how to name the file using attribute's
@@ -199,13 +201,16 @@
return fspath
def current_fs_path(self, entity, attr):
+ """return the current fs_path of the tribute.
+
+ Return None is the attr is not stored yet."""
sysource = entity._cw.pool.source('system')
cu = sysource.doexec(entity._cw,
'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % (
attr, entity.__regid__, entity.eid))
rawvalue = cu.fetchone()[0]
if rawvalue is None: # no previous value
- return self.new_fs_path(entity, attr)
+ return None
return sysource._process_value(rawvalue, cu.description[0],
binarywrap=str)
--- a/server/sqlutils.py Mon May 16 16:24:00 2011 +0200
+++ b/server/sqlutils.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -25,7 +25,7 @@
from logilab import database as db, common as lgc
from logilab.common.shellutils import ProgressBar
-from logilab.common.date import todate, todatetime
+from logilab.common.date import todate, todatetime, utcdatetime, utctime
from logilab.database.sqlgen import SQLGenerator
from cubicweb import Binary, ConfigurationError
@@ -204,6 +204,12 @@
def process_result(self, cursor, column_callbacks=None, session=None):
"""return a list of CubicWeb compliant values from data in the given cursor
"""
+ return list(self.iter_process_result(cursor, column_callbacks, session))
+
+ def iter_process_result(self, cursor, column_callbacks=None, session=None):
+ """return a iterator on tuples of CubicWeb compliant values from data
+ in the given cursor
+ """
# use two different implementations to avoid paying the price of
# callback lookup for each *cell* in results when there is nothing to
# lookup
@@ -219,16 +225,19 @@
process_value = self._process_value
binary = Binary
# /end
- results = cursor.fetchall()
- for i, line in enumerate(results):
- result = []
- for col, value in enumerate(line):
- if value is None:
- result.append(value)
- continue
- result.append(process_value(value, descr[col], encoding, binary))
- results[i] = result
- return results
+ cursor.arraysize = 100
+ while True:
+ results = cursor.fetchmany()
+ if not results:
+ break
+ for line in results:
+ result = []
+ for col, value in enumerate(line):
+ if value is None:
+ result.append(value)
+ continue
+ result.append(process_value(value, descr[col], encoding, binary))
+ yield result
def _cb_process_result(self, cursor, column_callbacks, session):
# begin bind to locals for optimization
@@ -237,22 +246,25 @@
process_value = self._process_value
binary = Binary
# /end
- results = cursor.fetchall()
- for i, line in enumerate(results):
- result = []
- for col, value in enumerate(line):
- if value is None:
+ cursor.arraysize = 100
+ while True:
+ results = cursor.fetchmany()
+ if not results:
+ break
+ for line in results:
+ result = []
+ for col, value in enumerate(line):
+ if value is None:
+ result.append(value)
+ continue
+ cbstack = column_callbacks.get(col, None)
+ if cbstack is None:
+ value = process_value(value, descr[col], encoding, binary)
+ else:
+ for cb in cbstack:
+ value = cb(self, session, value)
result.append(value)
- continue
- cbstack = column_callbacks.get(col, None)
- if cbstack is None:
- value = process_value(value, descr[col], encoding, binary)
- else:
- for cb in cbstack:
- value = cb(self, session, value)
- result.append(value)
- results[i] = result
- return results
+ yield result
def preprocess_entity(self, entity):
"""return a dictionary to use as extra argument to cursor.execute
@@ -274,10 +286,15 @@
value = crypt_password(value)
value = self._binary(value)
# XXX needed for sqlite but I don't think it is for other backends
- elif atype == 'Datetime' and isinstance(value, date):
+ # Note: use is __class__ since issubclass(datetime, date)
+ elif atype in ('Datetime', 'TZDatetime') and type(value) is date:
value = todatetime(value)
elif atype == 'Date' and isinstance(value, datetime):
value = todate(value)
+ elif atype == 'TZDatetime' and getattr(value, 'tzinfo', None):
+ value = utcdatetime(value)
+ elif atype == 'TZTime' and getattr(value, 'tzinfo', None):
+ value = utctime(value)
elif isinstance(value, Binary):
value = self._binary(value.getvalue())
attrs[SQL_PREFIX+str(attr)] = value
@@ -326,3 +343,13 @@
sqlite_hooks = SQL_CONNECT_HOOKS.setdefault('sqlite', [])
sqlite_hooks.append(init_sqlite_connexion)
+
+
+def init_postgres_connexion(cnx):
+ cnx.cursor().execute('SET TIME ZONE UTC')
+ # commit is needed, else setting are lost if the connection is first
+ # rollbacked
+ cnx.commit()
+
+postgres_hooks = SQL_CONNECT_HOOKS.setdefault('postgres', [])
+postgres_hooks.append(init_postgres_connexion)
--- a/server/ssplanner.py Mon May 16 16:24:00 2011 +0200
+++ b/server/ssplanner.py Wed Jul 20 18:21:47 2011 +0200
@@ -28,7 +28,6 @@
from cubicweb.schema import VIRTUAL_RTYPES
from cubicweb.rqlrewrite import add_types_restriction
from cubicweb.server.session import security_enabled
-from cubicweb.server.hook import CleanupDeletedEidsCacheOp
from cubicweb.server.edition import EditedEntity
READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity'))
@@ -521,13 +520,7 @@
if results:
todelete = frozenset(typed_eid(eid) for eid, in results)
session = self.plan.session
- # mark eids as being deleted in session info and setup cache update
- # operation (register pending eids before actual deletion to avoid
- # multiple call to glob_delete_entities)
- op = CleanupDeletedEidsCacheOp.get_instance(session)
- actual = todelete - op._container
- op._container |= actual
- session.repo.glob_delete_entities(session, actual)
+ session.repo.glob_delete_entities(session, todelete)
return results
class DeleteRelationsStep(Step):
@@ -559,6 +552,7 @@
session = self.plan.session
repo = session.repo
edefs = {}
+ relations = {}
# insert relations
if self.children:
result = self.execute_child()
@@ -578,9 +572,14 @@
edefs[eid] = edited = EditedEntity(edef)
edited.edited_attribute(str(rschema), rhsval)
else:
- repo.glob_add_relation(session, lhsval, str(rschema), rhsval)
+ str_rschema = str(rschema)
+ if str_rschema in relations:
+ relations[str_rschema].append((lhsval, rhsval))
+ else:
+ relations[str_rschema] = [(lhsval, rhsval)]
result[i] = newrow
# update entities
+ repo.glob_add_relations(session, relations)
for eid, edited in edefs.iteritems():
repo.glob_update_entity(session, edited)
return result
--- a/server/test/data/schema.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/data/schema.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -18,7 +18,7 @@
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
SubjectRelation, RichString, String, Int, Float,
- Boolean, Datetime)
+ Boolean, Datetime, TZDatetime)
from yams.constraints import SizeConstraint
from cubicweb.schema import (WorkflowableEntityType,
RQLConstraint, RQLUniqueConstraint,
@@ -114,6 +114,7 @@
tel = Int()
fax = Int()
datenaiss = Datetime()
+ tzdatenaiss = TZDatetime()
test = Boolean(__permissions__={
'read': ('managers', 'users', 'guests'),
'update': ('managers',),
@@ -219,3 +220,26 @@
class require_state(RelationDefinition):
subject = 'CWPermission'
object = 'State'
+
+class personne_composite(RelationDefinition):
+ subject='Personne'
+ object='Personne'
+ composite='subject'
+
+class personne_inlined(RelationDefinition):
+ subject='Personne'
+ object='Personne'
+ cardinality='?*'
+ inlined=True
+
+
+class login_user(RelationDefinition):
+ subject = 'Personne'
+ object = 'CWUser'
+ cardinality = '??'
+
+class ambiguous_inlined(RelationDefinition):
+ subject = ('Affaire', 'Note')
+ object = 'CWUser'
+ inlined = True
+ cardinality = '?*'
--- a/server/test/data/sources_fti Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-[system]
-
-db-driver = postgres
-db-host = localhost
-db-port =
-adapter = native
-db-name = cw_fti_test
-db-encoding = UTF-8
-db-user = syt
-db-password = syt
-
-[admin]
-login = admin
-password = gingkow
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/data/sources_postgres Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,14 @@
+[system]
+
+db-driver = postgres
+db-host = localhost
+db-port = 5433
+adapter = native
+db-name = cw_fti_test
+db-encoding = UTF-8
+db-user = syt
+db-password = syt
+
+[admin]
+login = admin
+password = gingkow
--- a/server/test/unittest_fti.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-from __future__ import with_statement
-
-import socket
-
-from logilab.common.testlib import SkipTest
-
-from cubicweb.devtools import ApptestConfiguration
-from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.selectors import is_instance
-from cubicweb.entities.adapters import IFTIndexableAdapter
-
-AT_LOGILAB = socket.gethostname().endswith('.logilab.fr')
-
-
-class PostgresFTITC(CubicWebTC):
- config = ApptestConfiguration('data', sourcefile='sources_fti')
-
- @classmethod
- def setUpClass(cls):
- if not AT_LOGILAB:
- raise SkipTest('XXX %s: require logilab configuration' % cls.__name__)
-
- def test_occurence_count(self):
- req = self.request()
- c1 = req.create_entity('Card', title=u'c1',
- content=u'cubicweb cubicweb cubicweb')
- c2 = req.create_entity('Card', title=u'c3',
- content=u'cubicweb')
- c3 = req.create_entity('Card', title=u'c2',
- content=u'cubicweb cubicweb')
- self.commit()
- self.assertEqual(req.execute('Card X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
- [[c1.eid], [c3.eid], [c2.eid]])
-
-
- def test_attr_weight(self):
- class CardIFTIndexableAdapter(IFTIndexableAdapter):
- __select__ = is_instance('Card')
- attr_weight = {'title': 'A'}
- with self.temporary_appobjects(CardIFTIndexableAdapter):
- req = self.request()
- c1 = req.create_entity('Card', title=u'c1',
- content=u'cubicweb cubicweb cubicweb')
- c2 = req.create_entity('Card', title=u'c2',
- content=u'cubicweb cubicweb')
- c3 = req.create_entity('Card', title=u'cubicweb',
- content=u'autre chose')
- self.commit()
- self.assertEqual(req.execute('Card X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
- [[c3.eid], [c1.eid], [c2.eid]])
-
- def test_entity_weight(self):
- class PersonneIFTIndexableAdapter(IFTIndexableAdapter):
- __select__ = is_instance('Personne')
- entity_weight = 2.0
- with self.temporary_appobjects(PersonneIFTIndexableAdapter):
- req = self.request()
- c1 = req.create_entity('Personne', nom=u'c1', prenom=u'cubicweb')
- c2 = req.create_entity('Comment', content=u'cubicweb cubicweb', comments=c1)
- c3 = req.create_entity('Comment', content=u'cubicweb cubicweb cubicweb', comments=c1)
- self.commit()
- self.assertEqual(req.execute('Any X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
- [[c1.eid], [c3.eid], [c2.eid]])
-
-
-if __name__ == '__main__':
- from logilab.common.testlib import unittest_main
- unittest_main()
--- a/server/test/unittest_ldapuser.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_ldapuser.py Wed Jul 20 18:21:47 2011 +0200
@@ -260,7 +260,8 @@
self.sexecute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"')
def test_multiple_entities_from_different_sources(self):
- self.create_user('cochon')
+ req = self.request()
+ self.create_user(req, 'cochon')
self.failUnless(self.sexecute('Any X,Y WHERE X login %(syt)s, Y login "cochon"', {'syt': SYT}))
def test_exists1(self):
@@ -274,16 +275,18 @@
self.assertEqual(rset.rows, [['admin', 'activated'], [SYT, 'activated']])
def test_exists2(self):
- self.create_user('comme')
- self.create_user('cochon')
+ req = self.request()
+ self.create_user(req, 'comme')
+ self.create_user(req, 'cochon')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
rset = self.sexecute('Any GN ORDERBY GN WHERE X in_group G, G name GN, '
'(G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon")))')
self.assertEqual(rset.rows, [['managers'], ['users']])
def test_exists3(self):
- self.create_user('comme')
- self.create_user('cochon')
+ req = self.request()
+ self.create_user(req, 'comme')
+ self.create_user(req, 'cochon')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
self.failUnless(self.sexecute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"'))
self.sexecute('SET X copain Y WHERE X login %(syt)s, Y login "cochon"', {'syt': SYT})
@@ -293,9 +296,10 @@
self.assertEqual(sorted(rset.rows), [['managers', 'admin'], ['users', 'comme'], ['users', SYT]])
def test_exists4(self):
- self.create_user('comme')
- self.create_user('cochon', groups=('users', 'guests'))
- self.create_user('billy')
+ req = self.request()
+ self.create_user(req, 'comme')
+ self.create_user(req, 'cochon', groups=('users', 'guests'))
+ self.create_user(req, 'billy')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
self.sexecute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "billy"')
@@ -315,9 +319,10 @@
self.assertEqual(sorted(rset.rows), sorted(all.rows))
def test_exists5(self):
- self.create_user('comme')
- self.create_user('cochon', groups=('users', 'guests'))
- self.create_user('billy')
+ req = self.request()
+ self.create_user(req, 'comme')
+ self.create_user(req, 'cochon', groups=('users', 'guests'))
+ self.create_user(req, 'billy')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
self.sexecute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
self.sexecute('SET X copain Y WHERE X login "comme", Y login "billy"')
@@ -347,7 +352,8 @@
sorted(r[0] for r in afeids + ueids))
def _init_security_test(self):
- self.create_user('iaminguestsgrouponly', groups=('guests',))
+ req = self.request()
+ self.create_user(req, 'iaminguestsgrouponly', groups=('guests',))
cnx = self.login('iaminguestsgrouponly')
return cnx.cursor()
--- a/server/test/unittest_migractions.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_migractions.py Wed Jul 20 18:21:47 2011 +0200
@@ -364,8 +364,9 @@
'X from_entity FE, FE name "Personne",'
'X ordernum O')]
expected = [u'nom', u'prenom', u'sexe', u'promo', u'ass', u'adel', u'titre',
- u'web', u'tel', u'fax', u'datenaiss', u'test', 'description', u'firstname',
- u'creation_date', 'cwuri', u'modification_date']
+ u'web', u'tel', u'fax', u'datenaiss', u'tzdatenaiss', u'test',
+ u'description', u'firstname',
+ u'creation_date', u'cwuri', u'modification_date']
self.assertEqual(rinorder, expected)
# test permissions synchronization ####################################
--- a/server/test/unittest_msplanner.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_msplanner.py Wed Jul 20 18:21:47 2011 +0200
@@ -1991,12 +1991,12 @@
])
def test_source_specified_2_0(self):
- self._test('Card X WHERE X cw_source S, NOT S eid 1',
- [('OneFetchStep', [('Any X WHERE X is Card',
- [{'X': 'Card'}])],
- None, None,
- [self.cards],{}, [])
- ])
+ # self._test('Card X WHERE X cw_source S, NOT S eid 1',
+ # [('OneFetchStep', [('Any X WHERE X is Card',
+ # [{'X': 'Card'}])],
+ # None, None,
+ # [self.cards],{}, [])
+ # ])
self._test('Card X WHERE NOT X cw_source S, S eid 1',
[('OneFetchStep', [('Any X WHERE X is Card',
[{'X': 'Card'}])],
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/unittest_postgres.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,83 @@
+from __future__ import with_statement
+
+import socket
+from datetime import datetime
+
+from logilab.common.testlib import SkipTest
+
+from cubicweb.devtools import ApptestConfiguration
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.selectors import is_instance
+from cubicweb.entities.adapters import IFTIndexableAdapter
+
+AT_LOGILAB = socket.gethostname().endswith('.logilab.fr') # XXX
+
+from unittest_querier import FixedOffset
+
+class PostgresFTITC(CubicWebTC):
+ @classmethod
+ def setUpClass(cls):
+ if not AT_LOGILAB: # XXX here until we can raise SkipTest in setUp to detect we can't connect to the db
+ raise SkipTest('XXX %s: require logilab configuration' % cls.__name__)
+ cls.config = ApptestConfiguration('data', sourcefile='sources_postgres',
+ apphome=cls.datadir)
+
+ def test_occurence_count(self):
+ req = self.request()
+ c1 = req.create_entity('Card', title=u'c1',
+ content=u'cubicweb cubicweb cubicweb')
+ c2 = req.create_entity('Card', title=u'c3',
+ content=u'cubicweb')
+ c3 = req.create_entity('Card', title=u'c2',
+ content=u'cubicweb cubicweb')
+ self.commit()
+ self.assertEqual(req.execute('Card X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
+ [[c1.eid], [c3.eid], [c2.eid]])
+
+
+ def test_attr_weight(self):
+ class CardIFTIndexableAdapter(IFTIndexableAdapter):
+ __select__ = is_instance('Card')
+ attr_weight = {'title': 'A'}
+ with self.temporary_appobjects(CardIFTIndexableAdapter):
+ req = self.request()
+ c1 = req.create_entity('Card', title=u'c1',
+ content=u'cubicweb cubicweb cubicweb')
+ c2 = req.create_entity('Card', title=u'c2',
+ content=u'cubicweb cubicweb')
+ c3 = req.create_entity('Card', title=u'cubicweb',
+ content=u'autre chose')
+ self.commit()
+ self.assertEqual(req.execute('Card X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
+ [[c3.eid], [c1.eid], [c2.eid]])
+
+ def test_entity_weight(self):
+ class PersonneIFTIndexableAdapter(IFTIndexableAdapter):
+ __select__ = is_instance('Personne')
+ entity_weight = 2.0
+ with self.temporary_appobjects(PersonneIFTIndexableAdapter):
+ req = self.request()
+ c1 = req.create_entity('Personne', nom=u'c1', prenom=u'cubicweb')
+ c2 = req.create_entity('Comment', content=u'cubicweb cubicweb', comments=c1)
+ c3 = req.create_entity('Comment', content=u'cubicweb cubicweb cubicweb', comments=c1)
+ self.commit()
+ self.assertEqual(req.execute('Any X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
+ [[c1.eid], [c3.eid], [c2.eid]])
+
+
+ def test_tz_datetime(self):
+ self.execute("INSERT Personne X: X nom 'bob', X tzdatenaiss %(date)s",
+ {'date': datetime(1977, 6, 7, 2, 0, tzinfo=FixedOffset(1))})
+ datenaiss = self.execute("Any XD WHERE X nom 'bob', X tzdatenaiss XD")[0][0]
+ self.assertEqual(datenaiss.tzinfo, None)
+ self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 1, 0))
+ self.commit()
+ self.execute("INSERT Personne X: X nom 'boby', X tzdatenaiss %(date)s",
+ {'date': datetime(1977, 6, 7, 2, 0)})
+ datenaiss = self.execute("Any XD WHERE X nom 'boby', X tzdatenaiss XD")[0][0]
+ self.assertEqual(datenaiss.tzinfo, None)
+ self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 2, 0))
+
+if __name__ == '__main__':
+ from logilab.common.testlib import unittest_main
+ unittest_main()
--- a/server/test/unittest_querier.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_querier.py Wed Jul 20 18:21:47 2011 +0200
@@ -18,7 +18,7 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""unit tests for modules cubicweb.server.querier and cubicweb.server.ssplanner
"""
-from datetime import date, datetime
+from datetime import date, datetime, timedelta, tzinfo
from logilab.common.testlib import TestCase, unittest_main
from rql import BadRQLQuery, RQLSyntaxError
@@ -32,6 +32,14 @@
from cubicweb.devtools.repotest import tuplify, BaseQuerierTC
from unittest_session import Variable
+class FixedOffset(tzinfo):
+ def __init__(self, hours=0):
+ self.hours = hours
+ def utcoffset(self, dt):
+ return timedelta(hours=self.hours)
+ def dst(self, dt):
+ return timedelta(0)
+
# register priority/severity sorting registered procedure
from rql.utils import register_function, FunctionDescr
@@ -761,18 +769,20 @@
rset = self.execute('Any N WHERE X is CWEType, X name N, X final %(val)s',
{'val': True})
self.assertEqual(sorted(r[0] for r in rset.rows), ['Boolean', 'Bytes',
- 'Date', 'Datetime',
- 'Decimal', 'Float',
- 'Int', 'Interval',
- 'Password', 'String',
- 'Time'])
+ 'Date', 'Datetime',
+ 'Decimal', 'Float',
+ 'Int', 'Interval',
+ 'Password', 'String',
+ 'TZDatetime', 'TZTime',
+ 'Time'])
rset = self.execute('Any N WHERE X is CWEType, X name N, X final TRUE')
self.assertEqual(sorted(r[0] for r in rset.rows), ['Boolean', 'Bytes',
- 'Date', 'Datetime',
- 'Decimal', 'Float',
- 'Int', 'Interval',
- 'Password', 'String',
- 'Time'])
+ 'Date', 'Datetime',
+ 'Decimal', 'Float',
+ 'Int', 'Interval',
+ 'Password', 'String',
+ 'TZDatetime', 'TZTime',
+ 'Time'])
def test_select_constant(self):
rset = self.execute('Any X, "toto" ORDERBY X WHERE X is CWGroup')
@@ -1213,11 +1223,11 @@
self.assertEqual(rset.description, [('CWUser',)])
def test_update_upassword(self):
- cursor = self.pool['system']
rset = self.execute("INSERT CWUser X: X login 'bob', X upassword %(pwd)s", {'pwd': 'toto'})
self.assertEqual(rset.description[0][0], 'CWUser')
rset = self.execute("SET X upassword %(pwd)s WHERE X is CWUser, X login 'bob'",
{'pwd': 'tutu'})
+ cursor = self.pool['system']
cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
% (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
passwd = str(cursor.fetchone()[0])
@@ -1227,7 +1237,16 @@
self.assertEqual(len(rset.rows), 1)
self.assertEqual(rset.description, [('CWUser',)])
- # non regression tests ####################################################
+ # ZT datetime tests ########################################################
+
+ def test_tz_datetime(self):
+ self.execute("INSERT Personne X: X nom 'bob', X tzdatenaiss %(date)s",
+ {'date': datetime(1977, 6, 7, 2, 0, tzinfo=FixedOffset(1))})
+ datenaiss = self.execute("Any XD WHERE X nom 'bob', X tzdatenaiss XD")[0][0]
+ self.assertEqual(datenaiss.tzinfo, None)
+ self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 1, 0))
+
+ # non regression tests #####################################################
def test_nonregr_1(self):
teid = self.execute("INSERT Tag X: X name 'tag'")[0][0]
@@ -1416,6 +1435,22 @@
def test_nonregr_final_norestr(self):
self.assertRaises(BadRQLQuery, self.execute, 'Date X')
+ def test_nonregr_eid_cmp(self):
+ peid1 = self.execute("INSERT Personne X: X nom 'bidule'")[0][0]
+ peid2 = self.execute("INSERT Personne X: X nom 'bidule'")[0][0]
+ rset = self.execute('Any X,Y WHERE X is Personne, Y is Personne, X nom XD, Y nom XD, X eid Z, Y eid > Z')
+ self.assertEqual(rset.rows, [[peid1, peid2]])
+ rset = self.execute('Any X,Y WHERE X nom XD, Y nom XD, X eid Z, Y eid > Z')
+ self.assertEqual(rset.rows, [[peid1, peid2]])
+
+ def test_nonregr_has_text_ambiguity_1(self):
+ peid = self.execute("INSERT CWUser X: X login 'bidule', X upassword 'bidule', X in_group G WHERE G name 'users'")[0][0]
+ aeid = self.execute("INSERT Affaire X: X ref 'bidule'")[0][0]
+ self.commit()
+ rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule"')
+ self.assertEqual(rset.rows, [[peid]])
+ rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule", X in_state S, S name SN')
+ self.assertEqual(rset.rows, [[peid]])
if __name__ == '__main__':
unittest_main()
--- a/server/test/unittest_repository.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_repository.py Wed Jul 20 18:21:47 2011 +0200
@@ -69,11 +69,12 @@
cu = self.session.system_sql('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
% (namecol, table, finalcol, namecol), {'final': 'TRUE'})
self.assertEqual(cu.fetchall(), [(u'Boolean',), (u'Bytes',),
- (u'Date',), (u'Datetime',),
- (u'Decimal',),(u'Float',),
- (u'Int',),
- (u'Interval',), (u'Password',),
- (u'String',), (u'Time',)])
+ (u'Date',), (u'Datetime',),
+ (u'Decimal',),(u'Float',),
+ (u'Int',),
+ (u'Interval',), (u'Password',),
+ (u'String',),
+ (u'TZDatetime',), (u'TZTime',), (u'Time',)])
sql = ("SELECT etype.cw_eid, etype.cw_name, cstr.cw_eid, rel.eid_to "
"FROM cw_CWUniqueTogetherConstraint as cstr, "
" relations_relation as rel, "
@@ -276,13 +277,16 @@
cnxid = repo.connect(self.admlogin, password=self.admpassword)
repo.execute(cnxid, 'INSERT CWUser X: X login "toto", X upassword "tutu", X in_group G WHERE G name "users"')
repo.commit(cnxid)
+ lock = threading.Lock()
+ lock.acquire()
# close has to be in the thread due to sqlite limitations
def close_in_a_few_moment():
- time.sleep(0.1)
+ lock.acquire()
repo.close(cnxid)
t = threading.Thread(target=close_in_a_few_moment)
t.start()
def run_transaction():
+ lock.release()
repo.execute(cnxid, 'DELETE CWUser X WHERE X login "toto"')
repo.commit(cnxid)
try:
@@ -327,7 +331,7 @@
self.assertEqual(len(constraints), 1)
cstr = constraints[0]
self.assert_(isinstance(cstr, RQLConstraint))
- self.assertEqual(cstr.restriction, 'O final TRUE')
+ self.assertEqual(cstr.expression, 'O final TRUE')
ownedby = schema.rschema('owned_by')
self.assertEqual(ownedby.objects('CWEType'), ('CWUser',))
@@ -684,5 +688,183 @@
req.cnx.commit()
self.assertEqual(cm.exception.errors, {'inline1-subject': u'RQLUniqueConstraint S type T, S inline1 A1, A1 todo_by C, Y type T, Y inline1 A2, A2 todo_by C failed'})
+ def test_add_relations_at_creation_with_del_existing_rel(self):
+ req = self.request()
+ person = req.create_entity('Personne', nom=u'Toto', prenom=u'Lanturlu', sexe=u'M')
+ users_rql = 'Any U WHERE U is CWGroup, U name "users"'
+ users = self.execute(users_rql).get_entity(0, 0)
+ req.create_entity('CWUser',
+ login=u'Toto',
+ upassword=u'firstname',
+ firstname=u'firstname',
+ surname=u'surname',
+ reverse_login_user=person,
+ in_group=users)
+ self.commit()
+
+
+class PerformanceTest(CubicWebTC):
+ def setup_database(self):
+ import logging
+ logger = logging.getLogger('cubicweb.session')
+ #logger.handlers = [logging.StreamHandler(sys.stdout)]
+ logger.setLevel(logging.INFO)
+ self.info = logger.info
+
+ def test_composite_deletion(self):
+ req = self.request()
+ personnes = []
+ t0 = time.time()
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
+ for j in xrange(0, 2000, 100):
+ abraham.set_relations(personne_composite=personnes[j:j+100])
+ t1 = time.time()
+ self.info('creation: %.2gs', (t1 - t0))
+ req.cnx.commit()
+ t2 = time.time()
+ self.info('commit creation: %.2gs', (t2 - t1))
+ self.execute('DELETE Personne P WHERE P eid %(eid)s', {'eid': abraham.eid})
+ t3 = time.time()
+ self.info('deletion: %.2gs', (t3 - t2))
+ req.cnx.commit()
+ t4 = time.time()
+ self.info("commit deletion: %2gs", (t4 - t3))
+
+ def test_add_relation_non_inlined(self):
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ req.cnx.commit()
+ t0 = time.time()
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M',
+ personne_composite=personnes[:100])
+ t1 = time.time()
+ self.info('creation: %.2gs', (t1 - t0))
+ for j in xrange(100, 2000, 100):
+ abraham.set_relations(personne_composite=personnes[j:j+100])
+ t2 = time.time()
+ self.info('more relations: %.2gs', (t2-t1))
+ req.cnx.commit()
+ t3 = time.time()
+ self.info('commit creation: %.2gs', (t3 - t2))
+
+ def test_add_relation_inlined(self):
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ req.cnx.commit()
+ t0 = time.time()
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M',
+ personne_inlined=personnes[:100])
+ t1 = time.time()
+ self.info('creation: %.2gs', (t1 - t0))
+ for j in xrange(100, 2000, 100):
+ abraham.set_relations(personne_inlined=personnes[j:j+100])
+ t2 = time.time()
+ self.info('more relations: %.2gs', (t2-t1))
+ req.cnx.commit()
+ t3 = time.time()
+ self.info('commit creation: %.2gs', (t3 - t2))
+
+
+ def test_session_add_relation(self):
+ """ to be compared with test_session_add_relations"""
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
+ req.cnx.commit()
+ t0 = time.time()
+ add_relation = self.session.add_relation
+ for p in personnes:
+ add_relation(abraham.eid, 'personne_composite', p.eid)
+ req.cnx.commit()
+ t1 = time.time()
+ self.info('add relation: %.2gs', t1-t0)
+
+ def test_session_add_relations (self):
+ """ to be compared with test_session_add_relation"""
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
+ req.cnx.commit()
+ t0 = time.time()
+ add_relations = self.session.add_relations
+ relations = [('personne_composite', [(abraham.eid, p.eid) for p in personnes])]
+ add_relations(relations)
+ req.cnx.commit()
+ t1 = time.time()
+ self.info('add relations: %.2gs', t1-t0)
+ def test_session_add_relation_inlined(self):
+ """ to be compared with test_session_add_relations"""
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
+ req.cnx.commit()
+ t0 = time.time()
+ add_relation = self.session.add_relation
+ for p in personnes:
+ add_relation(abraham.eid, 'personne_inlined', p.eid)
+ req.cnx.commit()
+ t1 = time.time()
+ self.info('add relation (inlined): %.2gs', t1-t0)
+
+ def test_session_add_relations_inlined (self):
+ """ to be compared with test_session_add_relation"""
+ req = self.request()
+ personnes = []
+ for i in xrange(2000):
+ p = req.create_entity('Personne', nom=u'Doe%03d'%i, prenom=u'John', sexe=u'M')
+ personnes.append(p)
+ abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
+ req.cnx.commit()
+ t0 = time.time()
+ add_relations = self.session.add_relations
+ relations = [('personne_inlined', [(abraham.eid, p.eid) for p in personnes])]
+ add_relations(relations)
+ req.cnx.commit()
+ t1 = time.time()
+ self.info('add relations (inlined): %.2gs', t1-t0)
+
+ def test_optional_relation_reset_1(self):
+ req = self.request()
+ p1 = req.create_entity('Personne', nom=u'Vincent')
+ p2 = req.create_entity('Personne', nom=u'Florent')
+ w = req.create_entity('Affaire', ref=u'wc')
+ w.set_relations(todo_by=[p1,p2])
+ w.clear_all_caches()
+ self.commit()
+ self.assertEqual(len(w.todo_by), 1)
+ self.assertEqual(w.todo_by[0].eid, p2.eid)
+
+ def test_optional_relation_reset_2(self):
+ req = self.request()
+ p1 = req.create_entity('Personne', nom=u'Vincent')
+ p2 = req.create_entity('Personne', nom=u'Florent')
+ w = req.create_entity('Affaire', ref=u'wc')
+ w.set_relations(todo_by=p1)
+ self.commit()
+ w.set_relations(todo_by=p2)
+ w.clear_all_caches()
+ self.commit()
+ self.assertEqual(len(w.todo_by), 1)
+ self.assertEqual(w.todo_by[0].eid, p2.eid)
+
+
if __name__ == '__main__':
unittest_main()
--- a/server/test/unittest_rql2sql.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_rql2sql.py Wed Jul 20 18:21:47 2011 +0200
@@ -210,6 +210,12 @@
FROM cw_Personne AS _P
LIMIT 20
OFFSET 10'''),
+ ("Any P ORDERBY N LIMIT 1 WHERE P is Personne, P travaille S, S eid %(eid)s, P nom N, P nom %(text)s",
+ '''SELECT _P.cw_eid
+FROM cw_Personne AS _P, travaille_relation AS rel_travaille0
+WHERE rel_travaille0.eid_from=_P.cw_eid AND rel_travaille0.eid_to=12345 AND _P.cw_nom=hip hop momo
+ORDER BY _P.cw_nom
+LIMIT 1'''),
]
@@ -536,6 +542,14 @@
'''SELECT _X.cw_eid
FROM cw_CWEType AS _X
WHERE _X.cw_description!=parent AND _X.cw_description!=_X.cw_name'''),
+
+ ('DISTINCT Any X, SUM(C) GROUPBY X ORDERBY SUM(C) DESC WHERE H todo_by X, H duration C',
+ '''SELECT DISTINCT rel_todo_by0.eid_to, SUM(_H.cw_duration)
+FROM cw_Affaire AS _H, todo_by_relation AS rel_todo_by0
+WHERE rel_todo_by0.eid_from=_H.cw_eid
+GROUP BY rel_todo_by0.eid_to
+ORDER BY 2 DESC'''),
+
]
ADVANCED_WITH_GROUP_CONCAT = [
@@ -631,7 +645,12 @@
("Personne X,Y where X nom NX, Y nom NX, X eid XE, not Y eid XE",
'''SELECT _X.cw_eid, _Y.cw_eid
FROM cw_Personne AS _X, cw_Personne AS _Y
-WHERE _Y.cw_nom=_X.cw_nom AND NOT (_Y.cw_eid=_X.cw_eid)''')
+WHERE _Y.cw_nom=_X.cw_nom AND NOT (_Y.cw_eid=_X.cw_eid)'''),
+
+ ('Any X,Y WHERE X is Personne, Y is Personne, X nom XD, Y nom XD, X eid Z, Y eid > Z',
+ '''SELECT _X.cw_eid, _Y.cw_eid
+FROM cw_Personne AS _X, cw_Personne AS _Y
+WHERE _Y.cw_nom=_X.cw_nom AND _Y.cw_eid>_X.cw_eid'''),
]
@@ -835,9 +854,9 @@
WHERE _X.cw_eid=12'''
),
("Any P WHERE X eid 12, P? concerne X, X todo_by S",
- '''SELECT rel_concerne0.eid_from
-FROM todo_by_relation AS rel_todo_by1 LEFT OUTER JOIN concerne_relation AS rel_concerne0 ON (rel_concerne0.eid_to=12)
-WHERE rel_todo_by1.eid_from=12'''
+ '''SELECT rel_concerne1.eid_from
+FROM todo_by_relation AS rel_todo_by0 LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_to=12)
+WHERE rel_todo_by0.eid_from=12'''
),
('Any GN, TN ORDERBY GN WHERE T tags G?, T name TN, G name GN',
@@ -909,7 +928,38 @@
('Any O,AD WHERE NOT S inline1 O, S eid 123, O todo_by AD?',
'''SELECT _O.cw_eid, rel_todo_by0.eid_to
FROM cw_Note AS _S, cw_Affaire AS _O LEFT OUTER JOIN todo_by_relation AS rel_todo_by0 ON (rel_todo_by0.eid_from=_O.cw_eid)
-WHERE (_S.cw_inline1 IS NULL OR _S.cw_inline1!=_O.cw_eid) AND _S.cw_eid=123''')
+WHERE (_S.cw_inline1 IS NULL OR _S.cw_inline1!=_O.cw_eid) AND _S.cw_eid=123'''),
+
+ ('Any X,AE WHERE X multisource_inlined_rel S?, S ambiguous_inlined A, A modification_date AE',
+ '''SELECT _X.cw_eid, _T0.C2
+FROM cw_Card AS _X LEFT OUTER JOIN (SELECT _S.cw_eid AS C0, _A.cw_eid AS C1, _A.cw_modification_date AS C2
+FROM cw_Affaire AS _S, cw_CWUser AS _A
+WHERE _S.cw_ambiguous_inlined=_A.cw_eid
+UNION ALL
+SELECT _S.cw_eid AS C0, _A.cw_eid AS C1, _A.cw_modification_date AS C2
+FROM cw_CWUser AS _A, cw_Note AS _S
+WHERE _S.cw_ambiguous_inlined=_A.cw_eid) AS _T0 ON (_X.cw_multisource_inlined_rel=_T0.C0)
+UNION ALL
+SELECT _X.cw_eid, _T0.C2
+FROM cw_Note AS _X LEFT OUTER JOIN (SELECT _S.cw_eid AS C0, _A.cw_eid AS C1, _A.cw_modification_date AS C2
+FROM cw_Affaire AS _S, cw_CWUser AS _A
+WHERE _S.cw_ambiguous_inlined=_A.cw_eid
+UNION ALL
+SELECT _S.cw_eid AS C0, _A.cw_eid AS C1, _A.cw_modification_date AS C2
+FROM cw_CWUser AS _A, cw_Note AS _S
+WHERE _S.cw_ambiguous_inlined=_A.cw_eid) AS _T0 ON (_X.cw_multisource_inlined_rel=_T0.C0)'''
+ ),
+
+ ('Any X,T,OT WHERE X tags T, OT? tags X, X is Tag, X eid 123',
+ '''SELECT rel_tags0.eid_from, rel_tags0.eid_to, rel_tags1.eid_from
+FROM tags_relation AS rel_tags0 LEFT OUTER JOIN tags_relation AS rel_tags1 ON (rel_tags1.eid_to=123)
+WHERE rel_tags0.eid_from=123'''),
+
+ ('Any CASE, CALIBCFG, CFG '
+ 'WHERE CASE eid 1, CFG ecrit_par CASE, CALIBCFG? ecrit_par CASE',
+ '''SELECT _CFG.cw_ecrit_par, _CALIBCFG.cw_eid, _CFG.cw_eid
+FROM cw_Note AS _CFG LEFT OUTER JOIN cw_Note AS _CALIBCFG ON (_CALIBCFG.cw_ecrit_par=_CFG.cw_ecrit_par)
+WHERE _CFG.cw_ecrit_par=1'''),
]
VIRTUAL_VARS = [
@@ -1201,7 +1251,7 @@
def _check(self, rql, sql, varmap=None, args=None):
if args is None:
- args = {'text': 'hip hop momo'}
+ args = {'text': 'hip hop momo', 'eid': 12345}
try:
union = self._prepare(rql)
r, nargs, cbs = self.o.generate(union, args,
@@ -1225,9 +1275,13 @@
yield self._check, rql, sql
def _checkall(self, rql, sql):
+ if isinstance(rql, tuple):
+ rql, args = rql
+ else:
+ args = None
try:
rqlst = self._prepare(rql)
- r, args, cbs = self.o.generate(rqlst)
+ r, args, cbs = self.o.generate(rqlst, args)
self.assertEqual((r.strip(), args), sql)
except Exception, ex:
print rql
@@ -1239,7 +1293,7 @@
return
def test1(self):
- self._checkall('Any count(RDEF) WHERE RDEF relation_type X, X eid %(x)s',
+ self._checkall(('Any count(RDEF) WHERE RDEF relation_type X, X eid %(x)s', {'x': None}),
("""SELECT COUNT(T1.C0) FROM (SELECT _RDEF.cw_eid AS C0
FROM cw_CWAttribute AS _RDEF
WHERE _RDEF.cw_relation_type=%(x)s
@@ -1250,7 +1304,7 @@
)
def test2(self):
- self._checkall('Any X WHERE C comments X, C eid %(x)s',
+ self._checkall(('Any X WHERE C comments X, C eid %(x)s', {'x': None}),
('''SELECT rel_comments0.eid_to
FROM comments_relation AS rel_comments0
WHERE rel_comments0.eid_from=%(x)s''', {})
@@ -1411,6 +1465,12 @@
WHERE ((EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0 WHERE rel_owned_by0.eid_from=_X.cw_eid AND rel_owned_by0.eid_to=1)) OR (((EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by2, cw_Affaire AS _D LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_from=_D.cw_eid) LEFT OUTER JOIN cw_Note AS _B ON (rel_concerne1.eid_to=_B.cw_eid) WHERE rel_owned_by2.eid_from=_B.cw_eid AND rel_owned_by2.eid_to=1 AND _X.cw_eid=_D.cw_eid)) OR (EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by4, cw_Affaire AS _F LEFT OUTER JOIN concerne_relation AS rel_concerne3 ON (rel_concerne3.eid_from=_F.cw_eid) LEFT OUTER JOIN cw_Societe AS _E ON (rel_concerne3.eid_to=_E.cw_eid) WHERE rel_owned_by4.eid_from=_E.cw_eid AND rel_owned_by4.eid_to=1 AND _X.cw_eid=_F.cw_eid))))))) AS _T0, cw_CWEType AS _ET, is_relation AS rel_is0
WHERE rel_is0.eid_from=_T0.C0 AND rel_is0.eid_to=_ET.cw_eid
GROUP BY _ET.cw_name'''),
+
+ ('Any A WHERE A ordernum O, A is CWAttribute WITH O BEING (Any MAX(O) WHERE A ordernum O, A is CWAttribute)',
+ '''SELECT _A.cw_eid
+FROM (SELECT MAX(_A.cw_ordernum) AS C0
+FROM cw_CWAttribute AS _A) AS _T0, cw_CWAttribute AS _A
+WHERE _A.cw_ordernum=_T0.C0'''),
)):
yield t
@@ -1576,7 +1636,7 @@
'''SELECT 1
WHERE NOT (EXISTS(SELECT 1 FROM in_group_relation AS rel_in_group0))''')
- def test_nonregr_subquery_missing_join(self):
+ def test_nonregr_outer_join_multiple(self):
self._check('Any COUNT(P1148),G GROUPBY G '
'WHERE G owned_by D, D eid 1122, K1148 bookmarked_by P1148, '
'K1148 eid 1148, P1148? in_group G',
@@ -1586,7 +1646,7 @@
GROUP BY _G.cw_eid'''
)
- def test_nonregr_subquery_missing_join2(self):
+ def test_nonregr_outer_join_multiple2(self):
self._check('Any COUNT(P1148),G GROUPBY G '
'WHERE G owned_by D, D eid 1122, K1148 bookmarked_by P1148?, '
'K1148 eid 1148, P1148? in_group G',
@@ -1595,6 +1655,16 @@
WHERE rel_owned_by0.eid_from=_G.cw_eid AND rel_owned_by0.eid_to=1122
GROUP BY _G.cw_eid''')
+ def test_groupby_orderby_insertion_dont_modify_intention(self):
+ self._check('Any YEAR(XECT)*100+MONTH(XECT), COUNT(X),SUM(XCE),AVG(XSCT-XECT) '
+ 'GROUPBY YEAR(XECT),MONTH(XECT) ORDERBY 1 '
+ 'WHERE X creation_date XSCT, X modification_date XECT, '
+ 'X ordernum XCE, X is CWAttribute',
+ '''SELECT ((CAST(EXTRACT(YEAR from _X.cw_modification_date) AS INTEGER) * 100) + CAST(EXTRACT(MONTH from _X.cw_modification_date) AS INTEGER)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date))
+FROM cw_CWAttribute AS _X
+GROUP BY CAST(EXTRACT(YEAR from _X.cw_modification_date) AS INTEGER),CAST(EXTRACT(MONTH from _X.cw_modification_date) AS INTEGER)
+ORDER BY 1'''),
+
class SqlServer2005SQLGeneratorTC(PostgresSQLGeneratorTC):
backend = 'sqlserver2005'
@@ -1697,10 +1767,61 @@
FROM cw_EmailAddress AS _O
WHERE NOT (EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email0 WHERE rel_use_email0.eid_from=1 AND rel_use_email0.eid_to=_O.cw_eid)) AND EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email1 WHERE rel_use_email1.eid_to=_O.cw_eid AND EXISTS(SELECT 1 FROM cw_CWGroup AS _D WHERE rel_use_email1.eid_from=2 AND NOT (EXISTS(SELECT 1 FROM in_group_relation AS rel_in_group2 WHERE rel_in_group2.eid_from=2 AND rel_in_group2.eid_to=_D.cw_eid)) AND _D.cw_name=guests))
ORDER BY 4 DESC'''),
+
+ ("Any P ORDERBY N LIMIT 1 WHERE P is Personne, P travaille S, S eid %(eid)s, P nom N, P nom %(text)s",
+ '''WITH orderedrows AS (
+SELECT
+_L01
+, ROW_NUMBER() OVER (ORDER BY _L01) AS __RowNumber
+FROM (
+SELECT _P.cw_eid AS _L01 FROM cw_Personne AS _P, travaille_relation AS rel_travaille0
+WHERE rel_travaille0.eid_from=_P.cw_eid AND rel_travaille0.eid_to=12345 AND _P.cw_nom=hip hop momo
+) AS _SQ1 )
+SELECT
+_L01
+FROM orderedrows WHERE
+__RowNumber <= 1'''),
+
+ ("Any P ORDERBY N LIMIT 1 WHERE P is Personne, P nom N",
+ '''WITH orderedrows AS (
+SELECT
+_L01
+, ROW_NUMBER() OVER (ORDER BY _L01) AS __RowNumber
+FROM (
+SELECT _P.cw_eid AS _L01 FROM cw_Personne AS _P
+) AS _SQ1 )
+SELECT
+_L01
+FROM orderedrows WHERE
+__RowNumber <= 1
+'''),
+
+ ("Any PN, N, P ORDERBY N LIMIT 1 WHERE P is Personne, P nom N, P prenom PN",
+ '''WITH orderedrows AS (
+SELECT
+_L01, _L02, _L03
+, ROW_NUMBER() OVER (ORDER BY _L02) AS __RowNumber
+FROM (
+SELECT _P.cw_prenom AS _L01, _P.cw_nom AS _L02, _P.cw_eid AS _L03 FROM cw_Personne AS _P
+) AS _SQ1 )
+SELECT
+_L01, _L02, _L03
+FROM orderedrows WHERE
+__RowNumber <= 1
+'''),
]
for t in self._parse(WITH_LIMIT):# + ADVANCED_WITH_LIMIT_OR_ORDERBY):
yield t
+ def test_groupby_orderby_insertion_dont_modify_intention(self):
+ self._check('Any YEAR(XECT)*100+MONTH(XECT), COUNT(X),SUM(XCE),AVG(XSCT-XECT) '
+ 'GROUPBY YEAR(XECT),MONTH(XECT) ORDERBY 1 '
+ 'WHERE X creation_date XSCT, X modification_date XECT, '
+ 'X ordernum XCE, X is CWAttribute',
+ '''SELECT ((YEAR(_X.cw_modification_date) * 100) + MONTH(_X.cw_modification_date)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date))
+FROM cw_CWAttribute AS _X
+GROUP BY YEAR(_X.cw_modification_date),MONTH(_X.cw_modification_date)
+ORDER BY 1'''),
class SqliteSQLGeneratorTC(PostgresSQLGeneratorTC):
@@ -1832,6 +1953,16 @@
FROM cw_CWUser AS _X
WHERE ((YEAR(_X.cw_creation_date)=2010) OR (_X.cw_creation_date IS NULL))''')
+ def test_groupby_orderby_insertion_dont_modify_intention(self):
+ self._check('Any YEAR(XECT)*100+MONTH(XECT), COUNT(X),SUM(XCE),AVG(XSCT-XECT) '
+ 'GROUPBY YEAR(XECT),MONTH(XECT) ORDERBY 1 '
+ 'WHERE X creation_date XSCT, X modification_date XECT, '
+ 'X ordernum XCE, X is CWAttribute',
+ '''SELECT ((YEAR(_X.cw_modification_date) * 100) + MONTH(_X.cw_modification_date)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date))
+FROM cw_CWAttribute AS _X
+GROUP BY YEAR(_X.cw_modification_date),MONTH(_X.cw_modification_date)
+ORDER BY 1'''),
+
class MySQLGenerator(PostgresSQLGeneratorTC):
@@ -1928,6 +2059,16 @@
FROM (SELECT 1) AS _T
WHERE NOT (EXISTS(SELECT 1 FROM in_group_relation AS rel_in_group0))''')
+ def test_groupby_orderby_insertion_dont_modify_intention(self):
+ self._check('Any YEAR(XECT)*100+MONTH(XECT), COUNT(X),SUM(XCE),AVG(XSCT-XECT) '
+ 'GROUPBY YEAR(XECT),MONTH(XECT) ORDERBY 1 '
+ 'WHERE X creation_date XSCT, X modification_date XECT, '
+ 'X ordernum XCE, X is CWAttribute',
+ '''SELECT ((EXTRACT(YEAR from _X.cw_modification_date) * 100) + EXTRACT(MONTH from _X.cw_modification_date)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date))
+FROM cw_CWAttribute AS _X
+GROUP BY EXTRACT(YEAR from _X.cw_modification_date),EXTRACT(MONTH from _X.cw_modification_date)
+ORDER BY 1'''),
+
class removeUnsusedSolutionsTC(TestCase):
def test_invariant_not_varying(self):
--- a/server/test/unittest_rqlannotation.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_rqlannotation.py Wed Jul 20 18:21:47 2011 +0200
@@ -18,13 +18,16 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""unit tests for modules cubicweb.server.rqlannotation"""
-from cubicweb.devtools import init_test_database
+from cubicweb.devtools import TestServerConfiguration, get_test_db_handler
from cubicweb.devtools.repotest import BaseQuerierTC
def setUpModule(*args):
+ handler = get_test_db_handler(TestServerConfiguration(
+ 'data2', apphome=SQLGenAnnotatorTC.datadir))
+ handler.build_db_cache()
global repo, cnx
- repo, cnx = init_test_database(apphome=SQLGenAnnotatorTC.datadir)
+ repo, cnx = handler.get_repo_and_cnx()
def tearDownModule(*args):
global repo, cnx
@@ -330,6 +333,13 @@
self.assertEqual(rqlst.defined_vars['N']._q_invariant, False)
self.assertEqual(rqlst.defined_vars['F']._q_invariant, True)
+ def test_nonregr_ambiguity_2(self):
+ rqlst = self._prepare('Any S,SN WHERE X has_text "tot", X in_state S, S name SN, X is CWUser')
+ # X use has_text but should not be invariant as ambiguous, and has_text
+ # may not be its principal
+ self.assertEqual(rqlst.defined_vars['X']._q_invariant, False)
+ self.assertEqual(rqlst.defined_vars['S']._q_invariant, False)
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/server/test/unittest_security.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_security.py Wed Jul 20 18:21:47 2011 +0200
@@ -29,7 +29,8 @@
def setup_database(self):
super(BaseSecurityTC, self).setup_database()
- self.create_user('iaminusersgrouponly')
+ req = self.request()
+ self.create_user(req, 'iaminusersgrouponly')
readoriggroups = self.schema['Personne'].permissions['read']
addoriggroups = self.schema['Personne'].permissions['add']
def fix_perm():
@@ -260,7 +261,8 @@
def test_user_can_change_its_upassword(self):
- ueid = self.create_user('user').eid
+ req = self.request()
+ ueid = self.create_user(req, 'user').eid
cnx = self.login('user')
cu = cnx.cursor()
cu.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
@@ -271,7 +273,8 @@
cnx.close()
def test_user_cant_change_other_upassword(self):
- ueid = self.create_user('otheruser').eid
+ req = self.request()
+ ueid = self.create_user(req, 'otheruser').eid
cnx = self.login('iaminusersgrouponly')
cu = cnx.cursor()
cu.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
--- a/server/test/unittest_undo.py Mon May 16 16:24:00 2011 +0200
+++ b/server/test/unittest_undo.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
@@ -21,11 +22,15 @@
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.transaction import *
+from cubicweb.server.sources.native import UndoException
+
+
class UndoableTransactionTC(CubicWebTC):
def setup_database(self):
+ req = self.request()
self.session.undo_actions = set('CUDAR')
- self.toto = self.create_user('toto', password='toto', groups=('users',),
+ self.toto = self.create_user(req, 'toto', password='toto', groups=('users',),
commit=False)
self.txuuid = self.commit()
@@ -167,7 +172,7 @@
['CWUser'])
self.assertEqual([et.name for et in toto.is_instance_of],
['CWUser'])
- # undoing shouldn't be visble in undoable transaction, and the undoed
+ # undoing shouldn't be visble in undoable transaction, and the undone
# transaction should be removed
txs = self.cnx.undoable_transactions()
self.assertEqual(len(txs), 2)
@@ -246,7 +251,8 @@
def test_undo_creation_integrity_1(self):
session = self.session
- tutu = self.create_user('tutu', commit=False)
+ req = self.request()
+ tutu = self.create_user(req, 'tutu', commit=False)
txuuid = self.commit()
email = self.request().create_entity('EmailAddress', address=u'tutu@cubicweb.org')
prop = self.request().create_entity('CWProperty', pkey=u'ui.default-text-format',
@@ -282,6 +288,15 @@
# test implicit 'replacement' of an inlined relation
+
+class UndoExceptionInUnicode(CubicWebTC):
+
+ # problem occurs in string manipulation for python < 2.6
+ def test___unicode__method(self):
+ u = UndoException(u"voilà")
+ self.assertIsInstance(unicode(u), unicode)
+
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/server/utils.py Mon May 16 16:24:00 2011 +0200
+++ b/server/utils.py Wed Jul 20 18:21:47 2011 +0200
@@ -128,14 +128,18 @@
(interval, func_name(func)))
self.interval = interval
def auto_restart_func(self=self, func=func, args=args):
+ restart = True
try:
func(*args)
- except:
+ except Exception:
logger = logging.getLogger('cubicweb.repository')
logger.exception('Unhandled exception in LoopTask %s', self.name)
raise
+ except BaseException:
+ restart = False
finally:
- self.start()
+ if restart:
+ self.start()
self.func = auto_restart_func
self.name = func_name(func)
--- a/sobjects/parsers.py Mon May 16 16:24:00 2011 +0200
+++ b/sobjects/parsers.py Wed Jul 20 18:21:47 2011 +0200
@@ -15,7 +15,21 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""datafeed parser for xml generated by cubicweb"""
+"""datafeed parser for xml generated by cubicweb
+
+Example of mapping for CWEntityXMLParser::
+
+ {u'CWUser': { # EntityType
+ (u'in_group', u'subject', u'link'): [ # (rtype, role, action)
+ (u'CWGroup', {u'linkattr': u'name'})], # -> rules = [(EntityType, options), ...]
+ (u'tags', u'object', u'link-or-create'): [ # (...)
+ (u'Tag', {u'linkattr': u'name'})], # -> ...
+ (u'use_email', u'subject', u'copy'): [ # (...)
+ (u'EmailAddress', {})] # -> ...
+ }
+ }
+
+"""
import urllib2
import StringIO
@@ -34,11 +48,12 @@
from cubicweb import ValidationError, typed_eid
from cubicweb.server.sources import datafeed
-def ensure_str_keys(dict):
- for key in dict:
- dict[str(key)] = dict.pop(key)
+def ensure_str_keys(dic):
+ for key in dic:
+ dic[str(key)] = dic.pop(key)
-# see cubicweb.web.views.xmlrss.SERIALIZERS
+# XXX see cubicweb.cwvreg.YAMS_TO_PY
+# XXX see cubicweb.web.views.xmlrss.SERIALIZERS
DEFAULT_CONVERTERS = BASE_CONVERTERS.copy()
DEFAULT_CONVERTERS['String'] = unicode
DEFAULT_CONVERTERS['Password'] = lambda x: x.encode('utf8')
@@ -76,7 +91,7 @@
typeddict[rschema.type] = converters[attrtype](stringdict[rschema])
return typeddict
-def _entity_etree(parent):
+def _parse_entity_etree(parent):
for node in list(parent):
try:
item = {'cwtype': unicode(node.tag),
@@ -92,20 +107,18 @@
rels = {}
for child in node:
role = child.get('role')
- if child.get('role'):
+ if role:
# relation
related = rels.setdefault(role, {}).setdefault(child.tag, [])
- related += [ritem for ritem, _ in _entity_etree(child)]
+ related += [ritem for ritem, _ in _parse_entity_etree(child)]
else:
# attribute
item[child.tag] = unicode(child.text)
yield item, rels
def build_search_rql(etype, attrs):
- restrictions = []
- for attr in attrs:
- restrictions.append('X %(attr)s %%(%(attr)s)s' % {'attr': attr})
- return 'Any X WHERE X is %s, %s' % (etype, ','.join(restrictions))
+ restrictions = ['X %(attr)s %%(%(attr)s)s'%{'attr': attr} for attr in attrs]
+ return 'Any X WHERE X is %s, %s' % (etype, ', '.join(restrictions))
def rtype_role_rql(rtype, role):
if role == 'object':
@@ -121,7 +134,7 @@
def _check_linkattr_option(action, options, eid, _):
if not 'linkattr' in options:
- msg = _("'%s' action require 'linkattr' option") % action
+ msg = _("'%s' action requires 'linkattr' option") % action
raise ValidationError(eid, {rn('options', 'subject'): msg})
@@ -230,19 +243,12 @@
break
self.source.info('GET %s', url)
stream = _OPENER.open(url)
- return _entity_etree(etree.parse(stream).getroot())
-
- def process_one(self, url):
- # XXX assert len(root.children) == 1
- for item, rels in self.parse(url):
- return self.process_item(item, rels)
+ return _parse_entity_etree(etree.parse(stream).getroot())
def process_item(self, item, rels):
- entity = self.extid2entity(str(item.pop('cwuri')),
- item.pop('cwtype'),
+ entity = self.extid2entity(str(item.pop('cwuri')), item.pop('cwtype'),
item=item)
- if not (self.created_during_pull(entity)
- or self.updated_during_pull(entity)):
+ if not (self.created_during_pull(entity) or self.updated_during_pull(entity)):
self.notify_updated(entity)
item.pop('eid')
# XXX check modification date
@@ -250,16 +256,16 @@
entity.set_attributes(**attrs)
for (rtype, role, action), rules in self.source.mapping.get(entity.__regid__, {}).iteritems():
try:
- rel = rels[role][rtype]
+ related_items = rels[role][rtype]
except KeyError:
- self.source.error('relation %s-%s doesn\'t seem exported in %s xml',
+ self.source.error('relation %s-%s not found in xml export of %s',
rtype, role, entity.__regid__)
continue
try:
actionmethod = self.action_methods[action]
except KeyError:
raise Exception('Unknown action %s' % action)
- actionmethod(entity, rtype, role, rel, rules)
+ actionmethod(entity, rtype, role, related_items, rules)
return entity
def before_entity_copy(self, entity, sourceparams):
@@ -267,89 +273,89 @@
attrs = extract_typed_attrs(entity.e_schema, sourceparams['item'])
entity.cw_edited.update(attrs)
- def related_copy(self, entity, rtype, role, value, rules):
+ def related_copy(self, entity, rtype, role, others, rules):
"""implementation of 'copy' action
Takes no option.
"""
assert not any(x[1] for x in rules), "'copy' action takes no option"
ttypes = set([x[0] for x in rules])
- value = [item for item in value if item['cwtype'] in ttypes]
+ others = [item for item in others if item['cwtype'] in ttypes]
eids = [] # local eids
- if not value:
+ if not others:
self._clear_relation(entity, rtype, role, ttypes)
return
- for item in value:
- eids.append(self.process_one(self._complete_url(item)).eid)
+ for item in others:
+ item, _rels = self._complete_item(item)
+ other_entity = self.process_item(item, [])
+ eids.append(other_entity.eid)
self._set_relation(entity, rtype, role, eids)
- def related_link(self, entity, rtype, role, value, rules):
+ def related_link(self, entity, rtype, role, others, rules):
"""implementation of 'link' action
requires an options to control search of the linked entity.
"""
for ttype, options in rules:
assert 'linkattr' in options, (
- "'link-or-create' action require a list of attributes used to "
+ "'link' action requires a list of attributes used to "
"search if the entity already exists")
- self._related_link(entity, rtype, role, ttype, value, [options['linkattr']],
- self._log_not_found)
+ self._related_link(entity, rtype, role, ttype, others, [options['linkattr']],
+ create_when_not_found=False)
- def related_link_or_create(self, entity, rtype, role, value, rules):
+ def related_link_or_create(self, entity, rtype, role, others, rules):
"""implementation of 'link-or-create' action
requires an options to control search of the linked entity.
"""
for ttype, options in rules:
assert 'linkattr' in options, (
- "'link-or-create' action require a list of attributes used to "
+ "'link-or-create' action requires a list of attributes used to "
"search if the entity already exists")
- self._related_link(entity, rtype, role, ttype, value, [options['linkattr']],
- self._create_not_found)
-
- def _log_not_found(self, entity, rtype, role, ritem, searchvalues):
- self.source.error('can find %s entity with attributes %s',
- ritem['cwtype'], searchvalues)
+ self._related_link(entity, rtype, role, ttype, others, [options['linkattr']],
+ create_when_not_found=True)
- def _create_not_found(self, entity, rtype, role, ritem, searchvalues):
- ensure_str_keys(searchvalues) # XXX necessary with python < 2.6
- return self._cw.create_entity(ritem['cwtype'], **searchvalues).eid
-
- def _related_link(self, entity, rtype, role, ttype, value, searchattrs,
- notfound_callback):
+ def _related_link(self, entity, rtype, role, ttype, others, searchattrs,
+ create_when_not_found):
+ def issubset(x,y):
+ return all(z in y for z in x)
eids = [] # local eids
- for item in value:
+ for item in others:
if item['cwtype'] != ttype:
continue
- if not all(attr in item for attr in searchattrs):
- # need to fetch related entity's xml
- ritems = list(self.parse(self._complete_url(item, False)))
- assert len(ritems) == 1, 'unexpected xml'
- ritem = ritems[0][0] # list of 2-uples
- assert all(attr in ritem for attr in searchattrs), \
- 'missing attribute, got %s expected keys %s' % (item, searchattrs)
- else:
- ritem = item
- kwargs = dict((attr, ritem[attr]) for attr in searchattrs)
+ if not issubset(searchattrs, item):
+ item, _rels = self._complete_item(item, False)
+ if not issubset(searchattrs, item):
+ self.source.error('missing attribute, got %s expected keys %s'
+ % item, searchattrs)
+ continue
+ kwargs = dict((attr, item[attr]) for attr in searchattrs)
rql = build_search_rql(item['cwtype'], kwargs)
rset = self._cw.execute(rql, kwargs)
- if rset:
- assert len(rset) == 1
+ if len(rset) > 1:
+ self.source.error('ambiguous link: found %s entity %s with attributes %s',
+ len(rset), item['cwtype'], kwargs)
+ elif len(rset) == 1:
eids.append(rset[0][0])
+ elif create_when_not_found:
+ ensure_str_keys(kwargs) # XXX necessary with python < 2.6
+ eids.append(self._cw.create_entity(item['cwtype'], **kwargs).eid)
else:
- eid = notfound_callback(entity, rtype, role, ritem, kwargs)
- if eid is not None:
- eids.append(eid)
+ self.source.error('can not find %s entity with attributes %s',
+ item['cwtype'], kwargs)
if not eids:
self._clear_relation(entity, rtype, role, (ttype,))
else:
self._set_relation(entity, rtype, role, eids)
- def _complete_url(self, item, add_relations=True):
+ def _complete_item(self, item, add_relations=True):
itemurl = item['cwuri'] + '?vid=xml'
- for rtype, role, _ in self.source.mapping.get(item['cwtype'], ()):
- itemurl += '&relation=%s_%s' % (rtype, role)
- return itemurl
+ if add_relations:
+ for rtype, role, _ in self.source.mapping.get(item['cwtype'], ()):
+ itemurl += '&relation=%s-%s' % (rtype, role)
+ item_rels = list(self.parse(itemurl))
+ assert len(item_rels) == 1
+ return item_rels[0]
def _clear_relation(self, entity, rtype, role, ttypes):
if entity.eid not in self.stats['created']:
@@ -361,15 +367,18 @@
{'x': entity.eid})
def _set_relation(self, entity, rtype, role, eids):
- eidstr = ','.join(str(eid) for eid in eids)
- rql = rtype_role_rql(rtype, role)
- self._cw.execute('DELETE %s, NOT Y eid IN (%s)' % (rql, eidstr),
- {'x': entity.eid})
- if role == 'object':
- rql = 'SET %s, Y eid IN (%s), NOT Y %s X' % (rql, eidstr, rtype)
- else:
- rql = 'SET %s, Y eid IN (%s), NOT X %s Y' % (rql, eidstr, rtype)
+ rqlbase = rtype_role_rql(rtype, role)
+ rql = 'DELETE %s' % rqlbase
+ if eids:
+ eidstr = ','.join(str(eid) for eid in eids)
+ rql += ', NOT Y eid IN (%s)' % eidstr
self._cw.execute(rql, {'x': entity.eid})
+ if eids:
+ if role == 'object':
+ rql = 'SET %s, Y eid IN (%s), NOT Y %s X' % (rqlbase, eidstr, rtype)
+ else:
+ rql = 'SET %s, Y eid IN (%s), NOT X %s Y' % (rqlbase, eidstr, rtype)
+ self._cw.execute(rql, {'x': entity.eid})
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__)
--- a/sobjects/test/unittest_email.py Mon May 16 16:24:00 2011 +0200
+++ b/sobjects/test/unittest_email.py Wed Jul 20 18:21:47 2011 +0200
@@ -54,7 +54,8 @@
self.failIf(rset.rowcount != 1, rset)
def test_security_check(self):
- self.create_user('toto')
+ req = self.request()
+ self.create_user(req, 'toto')
email1 = self.execute('INSERT EmailAddress E: E address "client@client.com", U use_email E WHERE U login "admin"')[0][0]
self.commit()
cnx = self.login('toto')
--- a/sobjects/test/unittest_notification.py Mon May 16 16:24:00 2011 +0200
+++ b/sobjects/test/unittest_notification.py Wed Jul 20 18:21:47 2011 +0200
@@ -84,7 +84,7 @@
def test_status_change_view(self):
req = self.request()
- u = self.create_user('toto', req=req)
+ u = self.create_user(req, 'toto')
u.cw_adapt_to('IWorkflowable').fire_transition('deactivate', comment=u'yeah')
self.failIf(MAILBOX)
self.commit()
--- a/sobjects/test/unittest_parsers.py Mon May 16 16:24:00 2011 +0200
+++ b/sobjects/test/unittest_parsers.py Wed Jul 20 18:21:47 2011 +0200
@@ -129,7 +129,7 @@
}
})
session = self.repo.internal_session()
- stats = dfsource.pull_data(session, force=True)
+ stats = dfsource.pull_data(session, force=True, raise_on_error=True)
self.assertEqual(sorted(stats.keys()), ['created', 'updated'])
self.assertEqual(len(stats['created']), 2)
self.assertEqual(stats['updated'], set())
@@ -156,12 +156,12 @@
self.assertEqual(tag.cwuri, 'http://testing.fr/cubicweb/%s' % tag.eid)
self.assertEqual(tag.cw_source[0].name, 'system')
- stats = dfsource.pull_data(session, force=True)
+ stats = dfsource.pull_data(session, force=True, raise_on_error=True)
self.assertEqual(stats['created'], set())
self.assertEqual(len(stats['updated']), 2)
self.repo._type_source_cache.clear()
self.repo._extid_cache.clear()
- stats = dfsource.pull_data(session, force=True)
+ stats = dfsource.pull_data(session, force=True, raise_on_error=True)
self.assertEqual(stats['created'], set())
self.assertEqual(len(stats['updated']), 2)
--- a/test/data/rewrite/schema.py Mon May 16 16:24:00 2011 +0200
+++ b/test/data/rewrite/schema.py Wed Jul 20 18:21:47 2011 +0200
@@ -27,8 +27,8 @@
'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')),
}
ref = String(fulltextindexed=True, indexed=True, maxsize=16)
- documented_by = SubjectRelation('Card')
- concerne = SubjectRelation(('Societe', 'Note'))
+ documented_by = SubjectRelation('Card', cardinality='1*')
+ concerne = SubjectRelation(('Societe', 'Note'), cardinality='1*')
class Societe(EntityType):
--- a/test/data/schema.py Mon May 16 16:24:00 2011 +0200
+++ b/test/data/schema.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -15,13 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""
-
-"""
from yams.buildobjs import (EntityType, String, SubjectRelation,
RelationDefinition)
-from cubicweb.schema import WorkflowableEntityType
+
+from cubicweb.schema import (WorkflowableEntityType,
+ RQLConstraint, RQLVocabularyConstraint)
+
class Personne(EntityType):
nom = String(required=True)
@@ -29,24 +29,49 @@
type = String()
travaille = SubjectRelation('Societe')
evaluee = SubjectRelation(('Note', 'Personne'))
- connait = SubjectRelation('Personne', symmetric=True)
+ connait = SubjectRelation(
+ 'Personne', symmetric=True,
+ constraints=[
+ RQLConstraint('NOT S identity O'),
+ # conflicting constraints, see cw_unrelated_rql tests in
+ # unittest_entity.py
+ RQLVocabularyConstraint('NOT (S connait P, P nom "toto")'),
+ RQLVocabularyConstraint('S travaille P, P nom "tutu"')])
+
class Societe(EntityType):
nom = String()
evaluee = SubjectRelation('Note')
+ fournit = SubjectRelation(('Service', 'Produit'), cardinality='1*')
+
+
+class Service(EntityType):
+ fabrique_par = SubjectRelation('Personne', cardinality='1*')
+
+
+class Produit(EntityType):
+ fabrique_par = SubjectRelation('Usine', cardinality='1*')
+
+
+class Usine(EntityType):
+ lieu = String(required=True)
+
class Note(EntityType):
type = String()
ecrit_par = SubjectRelation('Personne')
+
class SubNote(Note):
__specializes_schema__ = True
description = String()
+
class tags(RelationDefinition):
subject = 'Tag'
object = ('Personne', 'Note')
+
class evaluee(RelationDefinition):
subject = 'CWUser'
object = 'Note'
@@ -54,5 +79,3 @@
class StateFull(WorkflowableEntityType):
name = String()
-
-
--- a/test/unittest_entity.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_entity.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -19,7 +19,7 @@
"""unit tests for cubicweb.web.views.entities module"""
from datetime import datetime
-
+from logilab.common import tempattr
from cubicweb import Binary, Unauthorized
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.mttransforms import HAS_TAL
@@ -29,6 +29,17 @@
class EntityTC(CubicWebTC):
+ def setUp(self):
+ super(EntityTC, self).setUp()
+ self.backup_dict = {}
+ for cls in self.vreg['etypes'].iter_classes():
+ self.backup_dict[cls] = (cls.fetch_attrs, cls.fetch_order)
+
+ def tearDown(self):
+ super(EntityTC, self).tearDown()
+ for cls in self.vreg['etypes'].iter_classes():
+ cls.fetch_attrs, cls.fetch_order = self.backup_dict[cls]
+
def test_boolean_value(self):
e = self.vreg['etypes'].etype_class('CWUser')(self.request())
self.failUnless(e)
@@ -128,6 +139,27 @@
self.assertEqual(len(p.related('tags', 'object', limit=2)), 2)
self.assertEqual(len(p.related('tags', 'object')), 4)
+ def test_cw_instantiate_relation(self):
+ req = self.request()
+ p1 = req.create_entity('Personne', nom=u'di')
+ p2 = req.create_entity('Personne', nom=u'mascio')
+ t = req.create_entity('Tag', name=u't1', tags=p1)
+ self.assertItemsEqual(t.tags, [p1])
+ t = req.create_entity('Tag', name=u't2', tags=p1.eid)
+ self.assertItemsEqual(t.tags, [p1])
+ t = req.create_entity('Tag', name=u't3', tags=[p1, p2.eid])
+ self.assertItemsEqual(t.tags, [p1, p2])
+
+ def test_cw_instantiate_reverse_relation(self):
+ req = self.request()
+ t1 = req.create_entity('Tag', name=u't1')
+ t2 = req.create_entity('Tag', name=u't2')
+ p = req.create_entity('Personne', nom=u'di mascio', reverse_tags=t1)
+ self.assertItemsEqual(p.reverse_tags, [t1])
+ p = req.create_entity('Personne', nom=u'di mascio', reverse_tags=t1.eid)
+ self.assertItemsEqual(p.reverse_tags, [t1])
+ p = req.create_entity('Personne', nom=u'di mascio', reverse_tags=[t1, t2.eid])
+ self.assertItemsEqual(p.reverse_tags, [t1, t2])
def test_fetch_rql(self):
user = self.user()
@@ -136,17 +168,19 @@
Note = self.vreg['etypes'].etype_class('Note')
peschema = Personne.e_schema
seschema = Societe.e_schema
- peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '1*'
- peschema.subjrels['connait'].rdef(peschema, peschema).cardinality = '11'
- peschema.subjrels['evaluee'].rdef(peschema, Note.e_schema).cardinality = '1*'
- seschema.subjrels['evaluee'].rdef(seschema, Note.e_schema).cardinality = '1*'
- # testing basic fetch_attrs attribute
- self.assertEqual(Personne.fetch_rql(user),
- 'Any X,AA,AB,AC ORDERBY AA ASC '
- 'WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
- pfetch_attrs = Personne.fetch_attrs
- sfetch_attrs = Societe.fetch_attrs
+ torestore = []
+ for rdef, card in [(peschema.subjrels['travaille'].rdef(peschema, seschema), '1*'),
+ (peschema.subjrels['connait'].rdef(peschema, peschema), '11'),
+ (peschema.subjrels['evaluee'].rdef(peschema, Note.e_schema), '1*'),
+ (seschema.subjrels['evaluee'].rdef(seschema, Note.e_schema), '1*')]:
+ cm = tempattr(rdef, 'cardinality', card)
+ cm.__enter__()
+ torestore.append(cm)
try:
+ # testing basic fetch_attrs attribute
+ self.assertEqual(Personne.fetch_rql(user),
+ 'Any X,AA,AB,AC ORDERBY AA ASC '
+ 'WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
# testing unknown attributes
Personne.fetch_attrs = ('bloug', 'beep')
self.assertEqual(Personne.fetch_rql(user), 'Any X WHERE X is Personne')
@@ -158,9 +192,9 @@
# testing two non final relations
Personne.fetch_attrs = ('nom', 'prenom', 'travaille', 'evaluee')
self.assertEqual(Personne.fetch_rql(user),
- 'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC '
- 'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
- 'X evaluee AE?, AE modification_date AF')
+ 'Any X,AA,AB,AC,AD,AE ORDERBY AA ASC '
+ 'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
+ 'X evaluee AE?')
# testing one non final relation with recursion
Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
Societe.fetch_attrs = ('nom', 'evaluee')
@@ -185,8 +219,9 @@
'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB')
# XXX test unauthorized attribute
finally:
- Personne.fetch_attrs = pfetch_attrs
- Societe.fetch_attrs = sfetch_attrs
+ # fetch_attrs restored by generic tearDown
+ for cm in torestore:
+ cm.__exit__(None, None, None)
def test_related_rql_base(self):
Personne = self.vreg['etypes'].etype_class('Personne')
@@ -215,7 +250,7 @@
'WHERE E eid %(x)s, E tags X, X is IN (Personne), X nom AA, '
'X modification_date AB')
- def test_related_rql_ambigous_cant_use_fetch_order(self):
+ def test_related_rql_ambiguous_cant_use_fetch_order(self):
tag = self.vreg['etypes'].etype_class('Tag')(self.request())
for ttype in self.schema['tags'].objects():
self.vreg['etypes'].etype_class(ttype).fetch_attrs = ('modification_date',)
@@ -223,56 +258,106 @@
'Any X,AA ORDERBY AA DESC '
'WHERE E eid %(x)s, E tags X, X modification_date AA')
+ def test_related_rql_cant_fetch_ambiguous_rtype(self):
+ soc_etype = self.vreg['etypes'].etype_class('Societe')
+ soc = soc_etype(self.request())
+ soc_etype.fetch_attrs = ('fournit',)
+ self.vreg['etypes'].etype_class('Service').fetch_attrs = ('fabrique_par',)
+ self.vreg['etypes'].etype_class('Produit').fetch_attrs = ('fabrique_par',)
+ self.vreg['etypes'].etype_class('Usine').fetch_attrs = ('lieu',)
+ self.vreg['etypes'].etype_class('Personne').fetch_attrs = ('nom',)
+ # XXX should be improved: we could fetch fabrique_par object too
+ self.assertEqual(soc.cw_related_rql('fournit', 'subject'),
+ 'Any X WHERE E eid %(x)s, E fournit X')
+
def test_unrelated_rql_security_1_manager(self):
user = self.request().user
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
- 'WHERE NOT S use_email O, S eid %(x)s, '
+ 'WHERE NOT EXISTS(ZZ use_email O), S eid %(x)s, '
'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
def test_unrelated_rql_security_1_user(self):
- self.create_user('toto')
+ req = self.request()
+ self.create_user(req, 'toto')
self.login('toto')
- user = self.request().user
+ user = req.user
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
- 'WHERE NOT S use_email O, S eid %(x)s, '
+ 'WHERE NOT EXISTS(ZZ use_email O), S eid %(x)s, '
'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
user = self.execute('Any X WHERE X login "admin"').get_entity(0, 0)
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
- self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
- 'NOT EXISTS(S use_email O), S eid %(x)s, '
- 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
- 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+ self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
+ 'WHERE NOT EXISTS(ZZ use_email O, ZZ is CWUser), S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, A eid %(B)s, '
+ 'EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
def test_unrelated_rql_security_1_anon(self):
self.login('anon')
user = self.request().user
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
- self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
- 'NOT EXISTS(S use_email O), S eid %(x)s, '
- 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
- 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+ self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
+ 'WHERE NOT EXISTS(ZZ use_email O, ZZ is CWUser), S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, A eid %(B)s, '
+ 'EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
def test_unrelated_rql_security_2(self):
email = self.execute('INSERT EmailAddress X: X address "hop"').get_entity(0, 0)
rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
- self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA ASC '
- 'WHERE NOT S use_email O, O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD')
+ self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
+ 'WHERE NOT EXISTS(S use_email O), O eid %(x)s, S is CWUser, '
+ 'S login AA, S firstname AB, S surname AC, S modification_date AD')
self.login('anon')
email = self.execute('Any X WHERE X eid %(x)s', {'x': email.eid}).get_entity(0, 0)
rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
- 'WHERE NOT EXISTS(S use_email O), O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD, '
- 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+ 'WHERE NOT EXISTS(S use_email O), O eid %(x)s, S is CWUser, '
+ 'S login AA, S firstname AB, S surname AC, S modification_date AD, '
+ 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
def test_unrelated_rql_security_nonexistant(self):
self.login('anon')
email = self.vreg['etypes'].etype_class('EmailAddress')(self.request())
rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
- 'WHERE S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD, '
- 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+ 'WHERE S is CWUser, '
+ 'S login AA, S firstname AB, S surname AC, S modification_date AD, '
+ 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+
+ def test_unrelated_rql_constraints_creation_subject(self):
+ person = self.vreg['etypes'].etype_class('Personne')(self.request())
+ rql = person.cw_unrelated_rql('connait', 'Personne', 'subject')[0]
+ self.assertEqual(
+ rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'O is Personne, O nom AA, O prenom AB, O modification_date AC')
+
+ def test_unrelated_rql_constraints_creation_object(self):
+ person = self.vreg['etypes'].etype_class('Personne')(self.request())
+ rql = person.cw_unrelated_rql('connait', 'Personne', 'object')[0]
+ self.assertEqual(
+ rql, 'Any S,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'S is Personne, S nom AA, S prenom AB, S modification_date AC, '
+ 'NOT (S connait A, A nom "toto"), A is Personne, EXISTS(S travaille B, B nom "tutu")')
+
+ def test_unrelated_rql_constraints_edition_subject(self):
+ person = self.request().create_entity('Personne', nom=u'sylvain')
+ rql = person.cw_unrelated_rql('connait', 'Personne', 'subject')[0]
+ self.assertEqual(
+ rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'NOT EXISTS(S connait O), S eid %(x)s, O is Personne, '
+ 'O nom AA, O prenom AB, O modification_date AC, '
+ 'NOT S identity O')
+
+ def test_unrelated_rql_constraints_edition_object(self):
+ person = self.request().create_entity('Personne', nom=u'sylvain')
+ rql = person.cw_unrelated_rql('connait', 'Personne', 'object')[0]
+ self.assertEqual(
+ rql, 'Any S,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'NOT EXISTS(S connait O), O eid %(x)s, S is Personne, '
+ 'S nom AA, S prenom AB, S modification_date AC, '
+ 'NOT S identity O, NOT (S connait A, A nom "toto"), '
+ 'EXISTS(S travaille B, B nom "tutu")')
def test_unrelated_base(self):
req = self.request()
@@ -302,7 +387,8 @@
user = self.request().user
rset = user.unrelated('use_email', 'EmailAddress', 'subject')
self.assertEqual([x.address for x in rset.entities()], [u'hop'])
- self.create_user('toto')
+ req = self.request()
+ self.create_user(req, 'toto')
self.login('toto')
email = self.execute('Any X WHERE X eid %(x)s', {'x': email.eid}).get_entity(0, 0)
rset = email.unrelated('use_email', 'CWUser', 'object')
@@ -511,6 +597,15 @@
self.assertEqual(person.prenom, u'sylvain')
self.assertEqual(person.nom, u'thénault')
+ def test_set_relations(self):
+ req = self.request()
+ person = req.create_entity('Personne', nom=u'chauvat', prenom=u'nicolas')
+ note = req.create_entity('Note', type=u'x')
+ note.set_relations(ecrit_par=person)
+ note = req.create_entity('Note', type=u'y')
+ note.set_relations(ecrit_par=person.eid)
+ self.assertEqual(len(person.reverse_ecrit_par), 2)
+
def test_metainformation_and_external_absolute_url(self):
req = self.request()
note = req.create_entity('Note', type=u'z')
--- a/test/unittest_rqlrewrite.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_rqlrewrite.py Wed Jul 20 18:21:47 2011 +0200
@@ -33,7 +33,8 @@
config.bootstrap_cubes()
schema = config.load_schema()
from yams.buildobjs import RelationDefinition
- schema.add_relation_def(RelationDefinition(subject='Card', name='in_state', object='State', cardinality='1*'))
+ schema.add_relation_def(RelationDefinition(subject='Card', name='in_state',
+ object='State', cardinality='1*'))
rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
'has_text': 'fti'})
@@ -78,12 +79,22 @@
return rewriter.rewritten
def test_vrefs(node):
- vrefmap = {}
+ vrefmaps = {}
+ selects = []
for vref in node.iget_nodes(nodes.VariableRef):
- vrefmap.setdefault(vref.name, set()).add(vref)
- for var in node.defined_vars.itervalues():
- assert not (var.stinfo['references'] ^ vrefmap[var.name])
- assert (var.stinfo['references'])
+ stmt = vref.stmt
+ try:
+ vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
+ except KeyError:
+ vrefmaps[stmt] = {vref.name: set( (vref,) )}
+ selects.append(stmt)
+ assert node in selects
+ for stmt in selects:
+ for var in stmt.defined_vars.itervalues():
+ assert var.stinfo['references']
+ vrefmap = vrefmaps[stmt]
+ assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
+
class RQLRewriteTC(TestCase):
"""a faire:
@@ -95,10 +106,10 @@
"""
def test_base_var(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Card C')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
u"Any C WHERE C is Card, B eid %(D)s, "
"EXISTS(C in_state A, B in_group E, F require_state A, "
@@ -130,27 +141,31 @@
"E in_state D, D name 'subscribed'), D is State, E is CWUser)")
def test_simplified_rqlst(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Any 2') # this is the simplified rql st for Any X WHERE X eid 12
- rewrite(rqlst, {('2', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
u"Any 2 WHERE B eid %(C)s, "
"EXISTS(2 in_state A, B in_group D, E require_state A, "
"E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
- def test_optional_var_base(self):
- card_constraint = ('X in_state S, U in_group G, P require_state S,'
+ def test_optional_var_1(self):
+ constraint = ('X in_state S, U in_group G, P require_state S,'
'P name "read", P require_group G')
rqlst = parse('Any A,C WHERE A documented_by C?')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
"Any A,C WHERE A documented_by C?, A is Affaire "
"WITH C BEING "
"(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
"G require_group F), D eid %(A)s, C is Card)")
+
+ def test_optional_var_2(self):
+ constraint = ('X in_state S, U in_group G, P require_state S,'
+ 'P name "read", P require_group G')
rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
- rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {})
+ rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
self.failUnlessEqual(rqlst.as_string(),
"Any A,C,T WHERE A documented_by C?, A is Affaire "
"WITH C,T BEING "
@@ -158,6 +173,34 @@
"G require_state B, G name 'read', G require_group F), "
"D eid %(A)s, C is Card)")
+ def test_optional_var_3(self):
+ constraint1 = ('X in_state S, U in_group G, P require_state S,'
+ 'P name "read", P require_group G')
+ constraint2 = 'X in_state S, S name "public"'
+ rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
+ rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
+ self.failUnlessEqual(rqlst.as_string(),
+ "Any A,C,T WHERE A documented_by C?, A is Affaire "
+ "WITH C,T BEING (Any C,T WHERE C title T, "
+ "EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F), "
+ "D eid %(A)s, C is Card, "
+ "EXISTS(C in_state E, E name 'public'))")
+
+ def test_optional_var_4(self):
+ constraint1 = 'A created_by U, X documented_by A'
+ constraint2 = 'A created_by U, X concerne A'
+ constraint3 = 'X created_by U'
+ rqlst = parse('Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
+ rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
+ ('X', 'X'): (constraint3,),
+ ('Y', 'X'): (constraint3,)}, {})
+ self.failUnlessEqual(rqlst.as_string(),
+ u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
+ 'EXISTS(X created_by B), EXISTS(Y created_by B), '
+ 'X is Card, Y is IN(Division, Note, Societe) '
+ 'WITH LA BEING (Any LA WHERE EXISTS(A created_by B, LA documented_by A), '
+ 'B eid %(D)s, LA is Affaire, EXISTS(E created_by B, LA concerne E))')
+
def test_optional_var_inlined(self):
c1 = ('X require_permission P')
c2 = ('X inlined_card O, O require_permission P')
@@ -353,14 +396,21 @@
self.failUnlessEqual(rqlst.as_string(),
u"Any C WHERE C is Card, EXISTS(C owned_by A, A is CWUser)")
- def test_rqlexpr_not_relation1(self):
+ def test_rqlexpr_not_relation_1_1(self):
constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
self.failUnlessEqual(rqlst.as_string(),
u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
- def test_rqlexpr_not_relation2(self):
+ def test_rqlexpr_not_relation_1_2(self):
+ constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
+ rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
+ rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
+ self.failUnlessEqual(rqlst.as_string(),
+ u'Any A WHERE NOT EXISTS(A documented_by C, C is Card), A is Affaire, EXISTS(A owned_by B, B login "hop", B is CWUser)')
+
+ def test_rqlexpr_not_relation_2(self):
constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
rqlst = rqlhelper.parse('Affaire A WHERE NOT A documented_by C', annotate=False)
rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
--- a/test/unittest_rset.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_rset.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,5 @@
# coding: utf-8
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -63,6 +63,13 @@
result = list(attr_desc_iterator(parse(rql).children[0], idx, idx))
self.assertEqual(result, relations)
+ def test_subquery_callfunc(self):
+ rql = ('Any A,B,C,COUNT(D) GROUPBY A,B,C WITH A,B,C,D BEING '
+ '(Any YEAR(CD), MONTH(CD), S, X WHERE X is CWUser, X creation_date CD, X in_state S)')
+ rqlst = parse(rql)
+ select, col = rqlst.locate_subquery(2, 'CWUser', None)
+ result = list(attr_desc_iterator(select, col, 2))
+ self.assertEqual(result, [])
class ResultSetTC(CubicWebTC):
@@ -114,7 +121,6 @@
description=[['CWUser', 'String']] * 3)
rs.req = self.request()
rs.vreg = self.vreg
-
self.assertEqual(rs.limit(2).rows, [[12000, 'adim'], [13000, 'syt']])
rs2 = rs.limit(2, offset=1)
self.assertEqual(rs2.rows, [[13000, 'syt'], [14000, 'nico']])
--- a/test/unittest_schema.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_schema.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -26,7 +26,7 @@
from rql import RQLSyntaxError
-from yams import BadSchemaDefinition
+from yams import ValidationError, BadSchemaDefinition
from yams.constraints import SizeConstraint, StaticVocabularyConstraint
from yams.buildobjs import RelationDefinition, EntityType, RelationType
from yams.reader import PyFileReader
@@ -37,6 +37,7 @@
RQLExpression, ERQLExpression, RRQLExpression,
normalize_expression, order_eschemas, guess_rrqlexpr_mainvars)
from cubicweb.devtools import TestServerConfiguration as TestConfiguration
+from cubicweb.devtools.testlib import CubicWebTC
DATADIR = join(dirname(__file__), 'data')
@@ -166,10 +167,11 @@
'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig',
'CWUniqueTogetherConstraint', 'CWUser',
'ExternalUri', 'File', 'Float', 'Int', 'Interval', 'Note',
- 'Password', 'Personne',
+ 'Password', 'Personne', 'Produit',
'RQLExpression',
- 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
- 'Tag', 'Time', 'Transition', 'TrInfo',
+ 'Service', 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
+ 'Tag', 'TZDatetime', 'TZTime', 'Time', 'Transition', 'TrInfo',
+ 'Usine',
'Workflow', 'WorkflowTransition']
self.assertListEqual(sorted(expected_entities), entities)
relations = sorted([str(r) for r in schema.relations()])
@@ -188,14 +190,14 @@
'ecrit_par', 'eid', 'evaluee', 'expression', 'exprtype',
- 'final', 'firstname', 'for_user',
+ 'fabrique_par', 'final', 'firstname', 'for_user', 'fournit',
'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
'has_text',
'identity', 'in_group', 'in_state', 'indexed',
'initial_state', 'inlined', 'internationalizable', 'is', 'is_instance_of',
- 'label', 'last_login_time', 'latest_retrieval', 'login',
+ 'label', 'last_login_time', 'latest_retrieval', 'lieu', 'login',
'mainvars', 'match_host', 'modification_date',
@@ -239,7 +241,7 @@
self.failUnlessEqual(len(constraints), 1, constraints)
constraint = constraints[0]
self.failUnless(isinstance(constraint, RQLConstraint))
- self.failUnlessEqual(constraint.restriction, 'O final TRUE')
+ self.failUnlessEqual(constraint.expression, 'O final TRUE')
def test_fulltext_container(self):
schema = loader.load(config)
@@ -314,8 +316,17 @@
class GuessRrqlExprMainVarsTC(TestCase):
def test_exists(self):
- mainvars = guess_rrqlexpr_mainvars(normalize_expression('NOT EXISTS(O team_competition C, C level < 3)'))
- self.assertEqual(mainvars, 'O')
+ mainvars = guess_rrqlexpr_mainvars(normalize_expression('NOT EXISTS(O team_competition C, C level < 3, C concerns S)'))
+ self.assertEqual(mainvars, set(['S', 'O']))
+
+
+class RQLConstraintTC(CubicWebTC):
+ def test_user_constraint(self):
+ cstr = RQLConstraint('U identity O')
+ anoneid = self.execute('Any X WHERE X login "anon"')[0][0]
+ self.assertRaises(ValidationError, cstr.repo_check, self.session, 1, 'rel', anoneid)
+ self.assertEqual(cstr.repo_check(self.session, 1, self.session.user.eid),
+ None) # no validation error, constraint checked
if __name__ == '__main__':
--- a/test/unittest_selectors.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_selectors.py Wed Jul 20 18:21:47 2011 +0200
@@ -26,7 +26,7 @@
from cubicweb.appobject import Selector, AndSelector, OrSelector
from cubicweb.selectors import (is_instance, adaptable, match_user_groups,
multi_lines_rset, score_entity, is_in_state,
- on_transition)
+ on_transition, rql_condition)
from cubicweb.web import action
@@ -221,7 +221,7 @@
def test_is_in_state(self):
for state in ('created', 'validated', 'abandoned'):
selector = is_in_state(state)
- self.assertEqual(selector(None, self.req, self.rset),
+ self.assertEqual(selector(None, self.req, rset=self.rset),
state=="created")
self.adapter.fire_transition('validate')
@@ -229,75 +229,75 @@
self.assertEqual(self.adapter.state, 'validated')
selector = is_in_state('created')
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = is_in_state('validated')
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
selector = is_in_state('validated', 'abandoned')
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
selector = is_in_state('abandoned')
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
self.adapter.fire_transition('forsake')
self._commit()
self.assertEqual(self.adapter.state, 'abandoned')
selector = is_in_state('created')
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = is_in_state('validated')
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = is_in_state('validated', 'abandoned')
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
self.assertEqual(self.adapter.state, 'abandoned')
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
def test_is_in_state_unvalid_names(self):
selector = is_in_state("unknown")
with self.assertRaises(ValueError) as cm:
- selector(None, self.req, self.rset)
+ selector(None, self.req, rset=self.rset)
self.assertEqual(str(cm.exception),
"wf_test: unknown state(s): unknown")
selector = is_in_state("weird", "unknown", "created", "weird")
with self.assertRaises(ValueError) as cm:
- selector(None, self.req, self.rset)
+ selector(None, self.req, rset=self.rset)
self.assertEqual(str(cm.exception),
"wf_test: unknown state(s): unknown,weird")
def test_on_transition(self):
for transition in ('validate', 'forsake'):
selector = on_transition(transition)
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
self.adapter.fire_transition('validate')
self._commit()
self.assertEqual(self.adapter.state, 'validated')
selector = on_transition("validate")
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
selector = on_transition("validate", "forsake")
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
selector = on_transition("forsake")
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
self.adapter.fire_transition('forsake')
self._commit()
self.assertEqual(self.adapter.state, 'abandoned')
selector = on_transition("validate")
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = on_transition("validate", "forsake")
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
selector = on_transition("forsake")
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
def test_on_transition_unvalid_names(self):
selector = on_transition("unknown")
with self.assertRaises(ValueError) as cm:
- selector(None, self.req, self.rset)
+ selector(None, self.req, rset=self.rset)
self.assertEqual(str(cm.exception),
"wf_test: unknown transition(s): unknown")
selector = on_transition("weird", "unknown", "validate", "weird")
with self.assertRaises(ValueError) as cm:
- selector(None, self.req, self.rset)
+ selector(None, self.req, rset=self.rset)
self.assertEqual(str(cm.exception),
"wf_test: unknown transition(s): unknown,weird")
@@ -308,11 +308,11 @@
self.assertEqual(self.adapter.state, 'validated')
selector = on_transition("validate")
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = on_transition("validate", "forsake")
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
selector = on_transition("forsake")
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
class MatchUserGroupsTC(CubicWebTC):
@@ -328,7 +328,8 @@
self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions'])
try:
# login as a simple user
- self.create_user('john')
+ req = self.request()
+ self.create_user(req, 'john')
self.login('john')
# it should not be possible to use SomeAction not owned objects
req = self.request()
@@ -361,13 +362,13 @@
def test_default_op_in_selector(self):
expected = len(self.rset)
selector = multi_lines_rset(expected)
- self.assertEqual(selector(None, self.req, self.rset), 1)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 1)
self.assertEqual(selector(None, self.req, None), 0)
selector = multi_lines_rset(expected + 1)
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
self.assertEqual(selector(None, self.req, None), 0)
selector = multi_lines_rset(expected - 1)
- self.assertEqual(selector(None, self.req, self.rset), 0)
+ self.assertEqual(selector(None, self.req, rset=self.rset), 0)
self.assertEqual(selector(None, self.req, None), 0)
def test_without_rset(self):
@@ -398,7 +399,7 @@
for (expected, operator, assertion) in testdata:
selector = multi_lines_rset(expected, operator)
- yield self.assertEqual, selector(None, self.req, self.rset), assertion
+ yield self.assertEqual, selector(None, self.req, rset=self.rset), assertion
class ScoreEntitySelectorTC(CubicWebTC):
@@ -407,17 +408,31 @@
req = self.request()
rset = req.execute('Any E WHERE E eid 1')
selector = score_entity(lambda x: None)
- self.assertEqual(selector(None, req, rset), 0)
+ self.assertEqual(selector(None, req, rset=rset), 0)
selector = score_entity(lambda x: "something")
- self.assertEqual(selector(None, req, rset), 1)
+ self.assertEqual(selector(None, req, rset=rset), 1)
selector = score_entity(lambda x: object)
- self.assertEqual(selector(None, req, rset), 1)
+ self.assertEqual(selector(None, req, rset=rset), 1)
rset = req.execute('Any G LIMIT 2 WHERE G is CWGroup')
selector = score_entity(lambda x: 10)
- self.assertEqual(selector(None, req, rset), 20)
+ self.assertEqual(selector(None, req, rset=rset), 20)
selector = score_entity(lambda x: 10, once_is_enough=True)
- self.assertEqual(selector(None, req, rset), 10)
+ self.assertEqual(selector(None, req, rset=rset), 10)
+ def test_rql_condition_entity(self):
+ req = self.request()
+ selector = rql_condition('X identity U')
+ rset = req.user.as_rset()
+ self.assertEqual(selector(None, req, rset=rset), 1)
+ self.assertEqual(selector(None, req, entity=req.user), 1)
+ self.assertEqual(selector(None, req), 0)
+
+ def test_rql_condition_user(self):
+ req = self.request()
+ selector = rql_condition('U login "admin"', user_condition=True)
+ self.assertEqual(selector(None, req), 1)
+ selector = rql_condition('U login "toto"', user_condition=True)
+ self.assertEqual(selector(None, req), 0)
if __name__ == '__main__':
unittest_main()
--- a/test/unittest_uilib.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_uilib.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -22,14 +22,15 @@
import pkg_resources
-from logilab.common.testlib import TestCase, unittest_main
+
from unittest2 import skipIf
+from logilab.common.testlib import DocTest, TestCase, unittest_main
+
from cubicweb import uilib
lxml_version = pkg_resources.get_distribution('lxml').version.split('.')
-
class UILIBTC(TestCase):
def test_remove_tags(self):
@@ -185,6 +186,10 @@
self.assertMultiLineEqual(uilib.soup2xhtml(incoming, 'ascii'), expected)
+class DocTest(DocTest):
+ module = uilib
+
+
if __name__ == '__main__':
unittest_main()
--- a/test/unittest_utils.py Mon May 16 16:24:00 2011 +0200
+++ b/test/unittest_utils.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -21,7 +21,7 @@
import decimal
import datetime
-from logilab.common.testlib import TestCase, unittest_main
+from logilab.common.testlib import TestCase, DocTest, unittest_main
from cubicweb.utils import make_uid, UStringIO, SizeConstrainedList, RepeatList
from cubicweb.entity import Entity
@@ -156,5 +156,8 @@
self.assertEqual(self.encode(TestCase), 'null')
+class DocTest(DocTest):
+ from cubicweb import utils as module
+
if __name__ == '__main__':
unittest_main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/ajax_url0.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,3 @@
+<div id="ajaxroot">
+ <h1>Hello</h1>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/ajax_url1.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,6 @@
+<div id="ajaxroot">
+ <div class="ajaxHtmlHead">
+ <script src="http://foo.js" type="text/javascript"> </script>
+ </div>
+ <h1>Hello</h1>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/ajax_url2.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,7 @@
+<div id="ajaxroot">
+ <div class="ajaxHtmlHead">
+ <script src="http://foo.js" type="text/javascript"> </script>
+ <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
+ </div>
+ <h1>Hello</h1>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/ajaxresult.json Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,1 @@
+['foo', 'bar']
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_ajax.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,24 @@
+<html>
+ <head>
+ <!-- dependencies -->
+ <script type="text/javascript" src="../../data/jquery.js"></script>
+ <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.dom.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.htmlhelpers.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.ajax.js" type="text/javascript"></script>
+ <!-- qunit files -->
+ <script type="text/javascript" src="../../../devtools/data/qunit.js"></script>
+ <link rel="stylesheet" type="text/css" media="all" href="../../../devtools/data/qunit.css" />
+ <!-- test suite -->
+ <script src="cwmock.js" type="text/javascript"></script>
+ <script src="test_ajax.js" type="text/javascript"></script>
+ </head>
+ <body>
+ <div id="main"> </div>
+ <h1 id="qunit-header">cubicweb.ajax.js functions tests</h1>
+ <h2 id="qunit-banner"></h2>
+ <ol id="qunit-tests">
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_ajax.js Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,244 @@
+$(document).ready(function() {
+
+ module("ajax", {
+ setup: function() {
+ this.scriptsLength = $('head script[src]').length-1;
+ this.cssLength = $('head link[rel=stylesheet]').length-1;
+ // re-initialize cw loaded cache so that each tests run in a
+ // clean environment, have a lookt at _loadAjaxHtmlHead implementation
+ // in cubicweb.ajax.js for more information.
+ cw.loaded_src = [];
+ cw.loaded_href = [];
+ },
+ teardown: function() {
+ $('head script[src]:gt(' + this.scriptsLength + ')').remove();
+ $('head link[rel=stylesheet]:gt(' + this.cssLength + ')').remove();
+ }
+ });
+
+ function jsSources() {
+ return $.map($('head script[src]'), function(script) {
+ return script.getAttribute('src');
+ });
+ }
+
+ test('test simple h1 inclusion (ajax_url0.html)', function() {
+ expect(3);
+ equals(jQuery('#main').children().length, 0);
+ stop();
+ jQuery('#main').loadxhtml('/../ajax_url0.html', {
+ callback: function() {
+ equals(jQuery('#main').children().length, 1);
+ equals(jQuery('#main h1').html(), 'Hello');
+ start();
+ }
+ });
+ });
+
+ test('test simple html head inclusion (ajax_url1.html)', function() {
+ expect(6);
+ var scriptsIncluded = jsSources();
+ equals(jQuery.inArray('http://foo.js', scriptsIncluded), - 1);
+ stop();
+ jQuery('#main').loadxhtml('/../ajax_url1.html', {
+ callback: function() {
+ var origLength = scriptsIncluded.length;
+ scriptsIncluded = jsSources();
+ // check that foo.js has been *appended* to <head>
+ equals(scriptsIncluded.length, origLength + 1);
+ equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
+ // check that <div class="ajaxHtmlHead"> has been removed
+ equals(jQuery('#main').children().length, 1);
+ equals(jQuery('div.ajaxHtmlHead').length, 0);
+ equals(jQuery('#main h1').html(), 'Hello');
+ start();
+ }
+ });
+ });
+
+ test('test addCallback', function() {
+ expect(3);
+ equals(jQuery('#main').children().length, 0);
+ stop();
+ var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+ d.addCallback(function() {
+ equals(jQuery('#main').children().length, 1);
+ equals(jQuery('#main h1').html(), 'Hello');
+ start();
+ });
+ });
+
+ test('test callback after synchronous request', function() {
+ expect(1);
+ var deferred = new Deferred();
+ var result = jQuery.ajax({
+ url: './ajax_url0.html',
+ async: false,
+ beforeSend: function(xhr) {
+ deferred._req = xhr;
+ },
+ success: function(data, status) {
+ deferred.success(data);
+ }
+ });
+ stop();
+ deferred.addCallback(function() {
+ // add an assertion to ensure the callback is executed
+ ok(true, "callback is executed");
+ start();
+ });
+ });
+
+ test('test addCallback with parameters', function() {
+ expect(3);
+ equals(jQuery('#main').children().length, 0);
+ stop();
+ var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+ d.addCallback(function(data, req, arg1, arg2) {
+ equals(arg1, 'Hello');
+ equals(arg2, 'world');
+ start();
+ },
+ 'Hello', 'world');
+ });
+
+ test('test callback after synchronous request with parameters', function() {
+ var deferred = new Deferred();
+ var result = jQuery.ajax({
+ url: '/../ajax_url0.html',
+ async: false,
+ beforeSend: function(xhr) {
+ deferred._req = xhr;
+ },
+ success: function(data, status) {
+ deferred.success(data);
+ }
+ });
+ deferred.addCallback(function(data, req, arg1, arg2) {
+ // add an assertion to ensure the callback is executed
+ ok(true, "callback is executed");
+ equals(arg1, 'Hello');
+ equals(arg2, 'world');
+ },
+ 'Hello', 'world');
+ });
+
+ test('test addErrback', function() {
+ expect(1);
+ stop();
+ var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
+ d.addCallback(function() {
+ // throw an exception to start errback chain
+ throw new Error();
+ });
+ d.addErrback(function() {
+ ok(true, "errback is executed");
+ start();
+ });
+ });
+
+ test('test callback / errback execution order', function() {
+ expect(4);
+ var counter = 0;
+ stop();
+ var d = jQuery('#main').loadxhtml('/../ajax_url0.html', {
+ callback: function() {
+ equals(++counter, 1); // should be executed first
+ start();
+ }
+ });
+ d.addCallback(function() {
+ equals(++counter, 2); // should be executed and break callback chain
+ throw new Error();
+ });
+ d.addCallback(function() {
+ // should not be executed since second callback raised an error
+ ok(false, "callback is executed");
+ });
+ d.addErrback(function() {
+ // should be executed after the second callback
+ equals(++counter, 3);
+ });
+ d.addErrback(function() {
+ // should be executed after the first errback
+ equals(++counter, 4);
+ });
+ });
+
+ test('test already included resources are ignored (ajax_url1.html)', function() {
+ expect(10);
+ var scriptsIncluded = jsSources();
+ // NOTE:
+ equals(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
+ equals(jQuery('head link').length, 1);
+ /* use endswith because in pytest context we have an absolute path */
+ ok(jQuery('head link').attr('href').endswith('/qunit.css'));
+ stop();
+ jQuery('#main').loadxhtml('/../ajax_url1.html', {
+ callback: function() {
+ var origLength = scriptsIncluded.length;
+ scriptsIncluded = jsSources();
+ try {
+ // check that foo.js has been inserted in <head>
+ equals(scriptsIncluded.length, origLength + 1);
+ equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
+ // check that <div class="ajaxHtmlHead"> has been removed
+ equals(jQuery('#main').children().length, 1);
+ equals(jQuery('div.ajaxHtmlHead').length, 0);
+ equals(jQuery('#main h1').html(), 'Hello');
+ // qunit.css is not added twice
+ equals(jQuery('head link').length, 1);
+ /* use endswith because in pytest context we have an absolute path */
+ ok(jQuery('head link').attr('href').endswith('/qunit.css'));
+ } finally {
+ start();
+ }
+ }
+ });
+ });
+
+ test('test synchronous request loadRemote', function() {
+ var res = loadRemote('/../ajaxresult.json', {},
+ 'GET', true);
+ same(res, ['foo', 'bar']);
+ });
+
+ test('test event on CubicWeb', function() {
+ expect(1);
+ stop();
+ var events = null;
+ jQuery(CubicWeb).bind('server-response', function() {
+ // check that server-response event on CubicWeb is triggered
+ events = 'CubicWeb';
+ });
+ jQuery('#main').loadxhtml('/../ajax_url0.html', {
+ callback: function() {
+ equals(events, 'CubicWeb');
+ start();
+ }
+ });
+ });
+
+ test('test event on node', function() {
+ expect(3);
+ stop();
+ var nodes = [];
+ jQuery('#main').bind('server-response', function() {
+ nodes.push('node');
+ });
+ jQuery(CubicWeb).bind('server-response', function() {
+ nodes.push('CubicWeb');
+ });
+ jQuery('#main').loadxhtml('/../ajax_url0.html', {
+ callback: function() {
+ equals(nodes.length, 2);
+ // check that server-response event on CubicWeb is triggered
+ // only once and event server-response on node is triggered
+ equals(nodes[0], 'CubicWeb');
+ equals(nodes[1], 'node');
+ start();
+ }
+ });
+ });
+});
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_htmlhelpers.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <script type="text/javascript" src="../../data/jquery.js"></script>
+ <script src="../../data/cubicweb.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.htmlhelpers.js" type="text/javascript"></script>
+ <script type="text/javascript" src="qunit.js"></script>
+ <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
+ <script src="cwmock.js" type="text/javascript"></script>
+ <script src="test_htmlhelpers.js" type="text/javascript"></script>
+ </head>
+ <body>
+ <div id="main">
+ </div>
+ <h1 id="qunit-header">cubicweb.htmlhelpers.js functions tests</h1>
+ <h2 id="qunit-banner"></h2>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests">
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_htmlhelpers.js Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,36 @@
+$(document).ready(function() {
+
+ module("module2", {
+ setup: function() {
+ $('#main').append('<select id="theselect" multiple="multiple" size="2">' +
+ '</select>');
+ }
+ });
+
+ test("test first selected", function() {
+ $('#theselect').append('<option value="foo">foo</option>' +
+ '<option selected="selected" value="bar">bar</option>' +
+ '<option value="baz">baz</option>' +
+ '<option selected="selecetd"value="spam">spam</option>');
+ var selected = firstSelected(document.getElementById("theselect"));
+ equals(selected.value, 'bar');
+ });
+
+ test("test first selected 2", function() {
+ $('#theselect').append('<option value="foo">foo</option>' +
+ '<option value="bar">bar</option>' +
+ '<option value="baz">baz</option>' +
+ '<option value="spam">spam</option>');
+ var selected = firstSelected(document.getElementById("theselect"));
+ equals(selected, null);
+ });
+
+ module("visibilty");
+ test('toggleVisibility', function() {
+ $('#main').append('<div id="foo"></div>');
+ toggleVisibility('foo');
+ ok($('#foo').hasClass('hidden'), 'check hidden class is set');
+ });
+
+});
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_utils.html Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <script type="text/javascript" src="../../data/jquery.js"></script>
+ <script src="../../data/jquery.corner.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
+ <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
+ <script src="utils.js" type="text/javascript"></script>
+ <script type="text/javascript" src="qunit.js"></script>
+ <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
+ <script src="cwmock.js" type="text/javascript"></script>
+ <script src="test_utils.js" type="text/javascript"></script>
+ </head>
+ <body>
+ <div id="main">
+ </div>
+ <h1 id="qunit-header">cw.utils functions tests</h1>
+ <h2 id="qunit-banner"></h2>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests">
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/test_utils.js Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,92 @@
+$(document).ready(function() {
+
+ module("datetime");
+
+ test("test full datetime", function() {
+ equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18, 10, 30, 0, 0)),
+ '1986-04-18 10:30:00');
+ });
+
+ test("test only date", function() {
+ equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18)), '1986-04-18 00:00:00');
+ });
+
+ test("test null", function() {
+ equals(cw.utils.toISOTimestamp(null), null);
+ });
+
+ module("parsing");
+ test("test basic number parsing", function() {
+ var d = strptime('2008/08/08', '%Y/%m/%d');
+ same(datetuple(d), [2008, 8, 8, 0, 0]);
+ d = strptime('2008/8/8', '%Y/%m/%d');
+ same(datetuple(d), [2008, 8, 8, 0, 0]);
+ d = strptime('8/8/8', '%Y/%m/%d');
+ same(datetuple(d), [8, 8, 8, 0, 0]);
+ d = strptime('0/8/8', '%Y/%m/%d');
+ same(datetuple(d), [0, 8, 8, 0, 0]);
+ d = strptime('-10/8/8', '%Y/%m/%d');
+ same(datetuple(d), [-10, 8, 8, 0, 0]);
+ d = strptime('-35000', '%Y');
+ same(datetuple(d), [-35000, 1, 1, 0, 0]);
+ });
+
+ test("test custom format parsing", function() {
+ var d = strptime('2008-08-08', '%Y-%m-%d');
+ same(datetuple(d), [2008, 8, 8, 0, 0]);
+ d = strptime('2008 - ! 08: 08', '%Y - ! %m: %d');
+ same(datetuple(d), [2008, 8, 8, 0, 0]);
+ d = strptime('2008-08-08 12:14', '%Y-%m-%d %H:%M');
+ same(datetuple(d), [2008, 8, 8, 12, 14]);
+ d = strptime('2008-08-08 1:14', '%Y-%m-%d %H:%M');
+ same(datetuple(d), [2008, 8, 8, 1, 14]);
+ d = strptime('2008-08-08 01:14', '%Y-%m-%d %H:%M');
+ same(datetuple(d), [2008, 8, 8, 1, 14]);
+ });
+
+ module("sliceList");
+ test("test slicelist", function() {
+ var list = ['a', 'b', 'c', 'd', 'e', 'f'];
+ same(sliceList(list, 2), ['c', 'd', 'e', 'f']);
+ same(sliceList(list, 2, -2), ['c', 'd']);
+ same(sliceList(list, -3), ['d', 'e', 'f']);
+ same(sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
+ same(sliceList(list), list);
+ });
+
+ module("formContents", {
+ setup: function() {
+ $('#main').append('<form id="test-form"></form>');
+ }
+ });
+ // XXX test fckeditor
+ test("test formContents", function() {
+ $('#test-form').append('<input name="input-text" ' +
+ 'type="text" value="toto" />');
+ $('#test-form').append('<textarea rows="10" cols="30" '+
+ 'name="mytextarea">Hello World!</textarea> ');
+ $('#test-form').append('<input name="choice" type="radio" ' +
+ 'value="yes" />');
+ $('#test-form').append('<input name="choice" type="radio" ' +
+ 'value="no" checked="checked"/>');
+ $('#test-form').append('<input name="check" type="checkbox" ' +
+ 'value="yes" />');
+ $('#test-form').append('<input name="check" type="checkbox" ' +
+ 'value="no" checked="checked"/>');
+ $('#test-form').append('<select id="theselect" name="theselect" ' +
+ 'multiple="multiple" size="2"></select>');
+ $('#theselect').append('<option selected="selected" ' +
+ 'value="foo">foo</option>' +
+ '<option value="bar">bar</option>');
+ //Append an unchecked radio input : should not be in formContents list
+ $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
+ 'value="one" />');
+ $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
+ 'value="two"/>');
+ same(formContents($('#test-form')[0]), [
+ ['input-text', 'mytextarea', 'choice', 'check', 'theselect'],
+ ['toto', 'Hello World!', 'no', 'no', 'foo']
+ ]);
+ });
+});
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/jstests/utils.js Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,29 @@
+function datetuple(d) {
+ return [d.getFullYear(), d.getMonth()+1, d.getDate(),
+ d.getHours(), d.getMinutes()];
+}
+
+function pprint(obj) {
+ print('{');
+ for(k in obj) {
+ print(' ' + k + ' = ' + obj[k]);
+ }
+ print('}');
+}
+
+function arrayrepr(array) {
+ return '[' + array.join(', ') + ']';
+}
+
+function assertArrayEquals(array1, array2) {
+ if (array1.length != array2.length) {
+ throw new crosscheck.AssertionFailure(array1.join(', ') + ' != ' + array2.join(', '));
+ }
+ for (var i=0; i<array1.length; i++) {
+ if (array1[i] != array2[i]) {
+
+ throw new crosscheck.AssertionFailure(arrayrepr(array1) + ' and ' + arrayrepr(array2)
+ + ' differs at index ' + i);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/test_jscript.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,42 @@
+from cubicweb.devtools.qunit import QUnitTestCase, unittest_main
+
+from os import path as osp
+
+
+class JScript(QUnitTestCase):
+
+ all_js_tests = (
+ ("jstests/test_utils.js", (
+ "../../web/data/cubicweb.js",
+ "../../web/data/cubicweb.compat.js",
+ "../../web/data/cubicweb.python.js",
+ "jstests/utils.js",
+ ),
+ ),
+
+ ("jstests/test_htmlhelpers.js", (
+ "../../web/data/cubicweb.js",
+ "../../web/data/cubicweb.compat.js",
+ "../../web/data/cubicweb.python.js",
+ "../../web/data/cubicweb.htmlhelpers.js",
+ ),
+ ),
+
+ ("jstests/test_ajax.js", (
+ "../../web/data/cubicweb.python.js",
+ "../../web/data/cubicweb.js",
+ "../../web/data/cubicweb.compat.js",
+ "../../web/data/cubicweb.htmlhelpers.js",
+ "../../web/data/cubicweb.ajax.js",
+ ), (
+ "jstests/ajax_url0.html",
+ "jstests/ajax_url1.html",
+ "jstests/ajax_url2.html",
+ "jstests/ajaxresult.json",
+ ),
+ ),
+ )
+
+
+if __name__ == '__main__':
+ unittest_main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/test_windmill.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,12 @@
+# Run all scenarii found in windmill directory
+from os.path import join, dirname
+from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
+ unittest_main)
+
+class CubicWebWindmillUseCase(CubicWebWindmillUseCase):
+ #test_dir = join(dirname(__file__), "windmill/test_edit_relation.py")
+ pass
+
+
+if __name__ == '__main__':
+ unittest_main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/windmill/test_connexion.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,41 @@
+from cubicweb.devtools import DEFAULT_SOURCES
+LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
+
+# Generated by the windmill services transformer
+from windmill.authoring import WindmillTestClient
+
+def test_connect():
+ client = WindmillTestClient(__name__)
+
+ client.open(url=u'/')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
+ client.type(text=LOGIN, id=u'__login')
+ client.type(text=PASSWORD, id=u'__password')
+
+ client.execJS(js=u"$('#loginForm').submit()")
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.sleep(milliseconds=u'5000')
+ client.asserts.assertJS(js=u'$(\'.message\').text() == "welcome %s !"' % LOGIN)
+ client.open(url=u'/logout')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.open(url=u'/')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
+
+
+def test_wrong_connect():
+ client = WindmillTestClient(__name__)
+
+ client.open(url=u'/')
+ # XXX windmill wants to use its proxy internally on 403 :-(
+ #client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
+ #client.type(text=LOGIN, id=u'__login')
+ #client.type(text=u'novalidpassword', id=u'__password')
+ #client.click(value=u'log in')
+ client.open(url=u'/?__login=user&__password=nopassword')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertTextIn(validator=u'authentication failure', id=u'loginBox')
+ client.open(url=u'/')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/windmill/test_creation.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,58 @@
+from cubicweb.devtools import DEFAULT_SOURCES
+LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
+
+# Generated by the windmill services transformer
+from windmill.authoring import WindmillTestClient
+
+def test_creation():
+ client = WindmillTestClient(__name__)
+
+ client.open(url=u'/')
+ client.waits.forPageLoad(timeout=u'8000')
+ client.type(text=LOGIN, id=u'__login')
+ client.type(text=PASSWORD, id=u'__password')
+ client.click(value=u'log in')
+ client.waits.forPageLoad(timeout=u'20000')
+
+ # pre-condition
+ client.open(url=u'/cwuser/myuser')
+ client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "this resource does not exist"')
+ client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
+ client.asserts.assertJS(js=u'$(\'.searchMessage strong\').text() == "No result matching query"')
+
+ client.open(url=u'/manage')
+ client.open(url=u'/add/CWUser')
+ client.type(text=u'myuser', id=u'login-subject:A')
+ client.type(text=u'myuser', id=u'upassword-subject:A')
+ client.type(text=u'myuser', name=u'upassword-subject-confirm:A')
+ client.type(text=u'myuser', id=u'firstname-subject:A')
+ client.select(option=u'managers', id=u'from_in_group-subject:A')
+ client.click(id=u'cwinoutadd')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.click(id=u'adduse_email:Alink')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.type(text=u'myuser@logilab.fr', id=u'address-subject:B')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.sleep(milliseconds=u'5000')
+ client.asserts.assertJS(js=u'$(\'.message\').text() == "entity created"')
+ client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "myuser"')
+ client.waits.forPageLoad(timeout=u'8000')
+ client.open(url=u'/cwuser/myuser?vid=sameetypelist')
+ client.waits.forPageLoad(timeout=u'8000')
+ client.asserts.assertJS(js=u'$(\'#contentmain a\').text() == "myuser"')
+ client.open(url=u'/cwuser/myuser?vid=text')
+ client.waits.forPageLoad(timeout=u'8000')
+ client.asserts.assertJS(js=u'$(\'#contentmain\').text() == "\\nmyuser"')
+ client.open(url=u'/cwuser/myuser?vid=deleteconf')
+ client.waits.forElement(timeout=u'8000', value=u'button_delete')
+ client.click(value=u'button_delete')
+ client.waits.forPageLoad(timeout=u'8000')
+ client.open(url=u'/cwuser/myuser')
+ client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "this resource does not exist"')
+ client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
+ client.asserts.assertJS(js=u'$(\'.searchMessage strong\').text() == "No result matching query"')
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testfunc/test/windmill/test_edit_relation.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,82 @@
+from cubicweb.devtools import DEFAULT_SOURCES
+LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
+
+# Generated by the windmill services transformer
+from windmill.authoring import WindmillTestClient
+
+
+def test_edit_relation():
+ client = WindmillTestClient(__name__)
+
+ client.open(url=u'/logout')
+ client.open(url=u'/')
+ client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
+ client.type(text=LOGIN, id=u'__login')
+ client.type(text=PASSWORD, id=u'__password')
+ client.execJS(js=u"$('#loginForm').submit()")
+ client.waits.forPageLoad(timeout=u'20000')
+ client.open(url=u'/add/Folder')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(timeout=u'8000', id=u'name-subject:A')
+ client.click(id=u'name-subject:A')
+ client.type(text=u'folder1', id=u'name-subject:A')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'add add Folder filed_under Folder object', timeout=u'8000')
+ client.click(link=u'add add Folder filed_under Folder object')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(timeout=u'8000', id=u'name-subject:A')
+ client.click(id=u'name-subject:A')
+ client.type(text=u'subfolder1', id=u'name-subject:A')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'more actions', timeout=u'8000')
+ client.click(link=u'more actions')
+ client.click(link=u'copy')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.type(text=u'folder2', id=u'name-subject:A')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'modify', timeout=u'8000')
+ client.click(link=u'modify')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(timeout=u'8000', id=u'footer')
+ client.click(link=u'x')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'add add Folder filed_under Folder object', timeout=u'8000')
+ client.click(link=u'add add Folder filed_under Folder object')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.type(text=u'subfolder2', id=u'name-subject:A')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'subfolder2', timeout=u'8000')
+ client.click(link=u'subfolder2')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(link=u'modify', timeout=u'8000')
+ client.click(link=u'modify')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(timeout=u'8000', id=u'footer')
+ client.click(link=u'x')
+ client.select(xpath=u'//select', index=u'1')
+ #client.execJQuery(jquery=u'("select").trigger(\'change\')') # BUGGY freeze UI..
+ client.execJS(js=u'$("select").trigger(\'change\')')
+ client.waits.sleep(milliseconds=u'2000')
+ client.select(jquery=u'(\'select:contains("Search")\')[0]', option=u'Search for folder')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.click(link=u'folder1')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.waits.forElement(timeout=u'8000', value=u'button_ok')
+ client.click(value=u'button_ok')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertText(xpath=u'//h1', validator=u'subfolder2')
+ client.waits.forElement(link=u'folder_plural', timeout=u'8000')
+ client.click(link=u'folder_plural')
+ client.waits.forPageLoad(timeout=u'20000')
+ client.asserts.assertText(jquery=u"('#contentmain div a')[0]", validator=u'folder1')
+ client.asserts.assertText(jquery=u"('#contentmain div a')[1]", validator=u'folder2')
+ client.asserts.assertText(jquery=u"('#contentmain div a')[2]", validator=u'subfolder1')
+ client.asserts.assertText(jquery=u"('#contentmain div a')[3]", validator=u'subfolder2')
+ client.click(link=u'subfolder2')
+ client.click(link=u'modify')
+ client.click(link=u'folder1')
--- a/uilib.py Mon May 16 16:24:00 2011 +0200
+++ b/uilib.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -62,9 +62,9 @@
return value
if attrtype == 'Date':
return ustrftime(value, req.property_value('ui.date-format'))
- if attrtype == 'Time':
+ if attrtype in ('Time', 'TZTime'):
return ustrftime(value, req.property_value('ui.time-format'))
- if attrtype == 'Datetime':
+ if attrtype in ('Datetime', 'TZDatetime'):
if displaytime:
return ustrftime(value, req.property_value('ui.datetime-format'))
return ustrftime(value, req.property_value('ui.date-format'))
@@ -72,8 +72,9 @@
if value:
return req._('yes')
return req._('no')
- if attrtype == 'Float':
+ if attrtype in ('Float', 'Decimal'):
value = req.property_value('ui.float-format') % value
+ # XXX Interval
return unicode(value)
--- a/utils.py Mon May 16 16:24:00 2011 +0200
+++ b/utils.py Wed Jul 20 18:21:47 2011 +0200
@@ -130,11 +130,11 @@
class SizeConstrainedList(list):
- """simple list that makes sure the list does not get bigger
- than a given size.
+ """simple list that makes sure the list does not get bigger than a given
+ size.
- when the list is full and a new element is added, the first
- element of the list is removed before appending the new one
+ when the list is full and a new element is added, the first element of the
+ list is removed before appending the new one
>>> l = SizeConstrainedList(2)
>>> l.append(1)
@@ -142,6 +142,7 @@
>>> l
[1, 2]
>>> l.append(3)
+ >>> l
[2, 3]
"""
def __init__(self, maxsize):
@@ -364,17 +365,43 @@
self.doctype = u''
# xmldecl and html opening tag
self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
- self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
- 'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
- 'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
+ self._namespaces = [('xmlns', 'http://www.w3.org/1999/xhtml'),
+ ('xmlns:cubicweb','http://www.logilab.org/2008/cubicweb')]
+ self._htmlattrs = [('xml:lang', req.lang),
+ ('lang', req.lang)]
# keep main_stream's reference on req for easier text/html demoting
req.main_stream = self
+ def add_namespace(self, prefix, uri):
+ self._namespaces.append( (prefix, uri) )
+
+ def set_namespaces(self, namespaces):
+ self._namespaces = namespaces
+
+ def add_htmlattr(self, attrname, attrvalue):
+ self._htmlattrs.append( (attrname, attrvalue) )
+
+ def set_htmlattrs(self, attrs):
+ self._htmlattrs = attrs
+
+ def set_doctype(self, doctype, reset_xmldecl=True):
+ self.doctype = doctype
+ if reset_xmldecl:
+ self.xmldecl = u''
+
def write(self, data):
"""StringIO interface: this method will be assigned to self.w
"""
self.body.write(data)
+ @property
+ def htmltag(self):
+ attrs = ' '.join('%s="%s"' % (attr, xml_escape(value))
+ for attr, value in (self._namespaces + self._htmlattrs))
+ if attrs:
+ return '<html %s>' % attrs
+ return '<html>'
+
def getvalue(self):
"""writes HTML headers, closes </head> tag and writes HTML body"""
return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
@@ -382,7 +409,6 @@
self.head.getvalue(),
self.body.getvalue())
-
try:
# may not be there if cubicweb-web not installed
if sys.version_info < (2, 6):
--- a/view.py Mon May 16 16:24:00 2011 +0200
+++ b/view.py Wed Jul 20 18:21:47 2011 +0200
@@ -371,7 +371,7 @@
class EntityView(View):
"""base class for views applying on an entity (i.e. uniform result set)"""
__select__ = non_final_entity()
- category = 'entityview'
+ category = _('entityview')
def call(self, **kwargs):
if self.cw_rset is None:
@@ -392,7 +392,7 @@
"""
__select__ = none_rset()
- category = 'startupview'
+ category = _('startupview')
def html_headers(self):
"""return a list of html headers (eg something to be inserted between
@@ -436,7 +436,7 @@
"""base class for views applying on any non empty result sets"""
__select__ = nonempty_rset()
- category = 'anyrsetview'
+ category = _('anyrsetview')
def columns_labels(self, mainindex=0, tr=True):
if tr:
@@ -447,11 +447,14 @@
rqlstdescr = self.cw_rset.syntax_tree().get_description(mainindex,
translate)[0]
labels = []
- for colindex, label in enumerate(rqlstdescr):
- # compute column header
- if label == 'Any': # find a better label
- label = ','.join(translate(et)
- for et in self.cw_rset.column_types(colindex))
+ for colidx, label in enumerate(rqlstdescr):
+ try:
+ label = getattr(self, 'label_column_%s' % colidx)()
+ except AttributeError:
+ # compute column header
+ if label == 'Any': # find a better label
+ label = ','.join(translate(et)
+ for et in self.cw_rset.column_types(colidx))
labels.append(label)
return labels
--- a/web/_exceptions.py Mon May 16 16:24:00 2011 +0200
+++ b/web/_exceptions.py Wed Jul 20 18:21:47 2011 +0200
@@ -53,6 +53,9 @@
self.status = int(status)
self.content = content
+ def __repr__(self):
+ return '%s(%r, %r)' % (self.__class__.__name__, self.status, self.content)
+
class InvalidSession(CubicWebException):
"""raised when a session id is found but associated session is not found or
invalid
--- a/web/application.py Mon May 16 16:24:00 2011 +0200
+++ b/web/application.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -71,6 +71,8 @@
total += 1
try:
last_usage_time = session.cnx.check()
+ except AttributeError:
+ last_usage_time = session.mtime
except BadConnectionId:
self.close_session(session)
closed += 1
@@ -204,17 +206,36 @@
except InvalidSession:
# try to open a new session, so we get an anonymous session if
# allowed
- try:
- session = self.open_session(req)
- except AuthenticationError:
- req.remove_cookie(cookie, sessioncookie)
- raise
+ session = self.open_session(req)
+ else:
+ if not session.cnx:
+ # session exists but is not bound to a connection. We should
+ # try to authenticate
+ loginsucceed = False
+ try:
+ if self.open_session(req, allow_no_cnx=False):
+ loginsucceed = True
+ except Redirect:
+ # may be raised in open_session (by postlogin mechanism)
+ # on successful connection
+ loginsucceed = True
+ raise
+ except AuthenticationError:
+ # authentication failed, continue to use this session
+ req.set_session(session)
+ finally:
+ if loginsucceed:
+ # session should be replaced by new session created
+ # in open_session
+ self.session_manager.close_session(session)
def get_session(self, req, sessionid):
- return self.session_manager.get_session(req, sessionid)
+ session = self.session_manager.get_session(req, sessionid)
+ session.mtime = time()
+ return session
- def open_session(self, req):
- session = self.session_manager.open_session(req)
+ def open_session(self, req, allow_no_cnx=True):
+ session = self.session_manager.open_session(req, allow_no_cnx=allow_no_cnx)
cookie = req.get_cookie()
sessioncookie = self.session_cookie(req)
cookie[sessioncookie] = session.sessionid
@@ -279,10 +300,7 @@
sessions (i.e. a new connection may be created or an already existing
one may be reused
"""
- try:
- self.session_handler.set_session(req)
- except AuthenticationError:
- req.set_session(DBAPISession(None))
+ self.session_handler.set_session(req)
# publish methods #########################################################
@@ -365,11 +383,12 @@
# redirect is raised by edit controller when everything went fine,
# so try to commit
try:
- txuuid = req.cnx.commit()
- if txuuid is not None:
- msg = u'<span class="undo">[<a href="%s">%s</a>]</span>' %(
- req.build_url('undo', txuuid=txuuid), req._('undo'))
- req.append_to_redirect_message(msg)
+ if req.cnx:
+ txuuid = req.cnx.commit()
+ if txuuid is not None:
+ msg = u'<span class="undo">[<a href="%s">%s</a>]</span>' %(
+ req.build_url('undo', txuuid=txuuid), req._('undo'))
+ req.append_to_redirect_message(msg)
except ValidationError, ex:
self.validation_error_handler(req, ex)
except Unauthorized, ex:
--- a/web/component.py Mon May 16 16:24:00 2011 +0200
+++ b/web/component.py Wed Jul 20 18:21:47 2011 +0200
@@ -410,7 +410,6 @@
params.pop('view', None)
params.pop('entity', None)
form = params.pop('formparams', {})
- form['pageid'] = self._cw.pageid
if entity.has_eid():
eid = entity.eid
else:
--- a/web/data/cubicweb.ajax.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.ajax.js Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-/* copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+/* copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
* contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
*
* This file is part of CubicWeb.
@@ -215,7 +215,7 @@
}
/**
- * .. function:: loadxhtml(url, form, reqtype='get', mode='replace', cursor=true)
+ * .. function:: loadxhtml(url, form, reqtype='get', mode='replace', cursor=false)
*
* build url given by absolute or relative `url` and `form` parameters
* (dictionary), fetch it using `reqtype` method, then evaluate the
@@ -231,7 +231,9 @@
*/
jQuery.fn.loadxhtml = function(url, form, reqtype, mode, cursor) {
if (this.size() > 1) {
- cw.log('loadxhtml was called with more than one element');
+ cw.log('loadxhtml called with more than one element');
+ } else if (this.size() < 1) {
+ cw.log('loadxhtml called without an element');
}
var callback = null;
if (form && form.callback) {
@@ -605,7 +607,7 @@
var ajaxArgs = ['render', formparams, registry, compid];
ajaxArgs = ajaxArgs.concat(cw.utils.sliceList(arguments, 4));
var params = ajaxFuncArgs.apply(null, ajaxArgs);
- $('#'+domid).loadxhtml('json', params, null, 'swap');
+ return $('#'+domid).loadxhtml('json', params, null, 'swap');
}
/* ajax tabs ******************************************************************/
@@ -700,43 +702,37 @@
}
);
-remoteExec = cw.utils.deprecatedFunction(
- '[3.9] remoteExec() is deprecated, use loadRemote instead',
- function(fname /* ... */) {
- setProgressCursor();
- var props = {
- fname: fname,
- pageid: pageid,
- arg: $.map(cw.utils.sliceList(arguments, 1), jQuery.toJSON)
- };
- var result = jQuery.ajax({
- url: JSON_BASE_URL,
- data: props,
- async: false,
- traditional: true
- }).responseText;
- if (result) {
- result = cw.evalJSON(result);
- }
- resetCursor();
- return result;
+function remoteExec(fname /* ... */) {
+ setProgressCursor();
+ var props = {
+ fname: fname,
+ pageid: pageid,
+ arg: $.map(cw.utils.sliceList(arguments, 1), jQuery.toJSON)
+ };
+ var result = jQuery.ajax({
+ url: JSON_BASE_URL,
+ data: props,
+ async: false,
+ traditional: true
+ }).responseText;
+ if (result) {
+ result = cw.evalJSON(result);
}
-);
+ resetCursor();
+ return result;
+}
-asyncRemoteExec = cw.utils.deprecatedFunction(
- '[3.9] asyncRemoteExec() is deprecated, use loadRemote instead',
- function(fname /* ... */) {
- setProgressCursor();
- var props = {
- fname: fname,
- pageid: pageid,
- arg: $.map(cw.utils.sliceList(arguments, 1), jQuery.toJSON)
- };
- // XXX we should inline the content of loadRemote here
- var deferred = loadRemote(JSON_BASE_URL, props, 'POST');
- deferred = deferred.addErrback(remoteCallFailed);
- deferred = deferred.addErrback(resetCursor);
- deferred = deferred.addCallback(resetCursor);
- return deferred;
- }
-);
+function asyncRemoteExec(fname /* ... */) {
+ setProgressCursor();
+ var props = {
+ fname: fname,
+ pageid: pageid,
+ arg: $.map(cw.utils.sliceList(arguments, 1), jQuery.toJSON)
+ };
+ // XXX we should inline the content of loadRemote here
+ var deferred = loadRemote(JSON_BASE_URL, props, 'POST');
+ deferred = deferred.addErrback(remoteCallFailed);
+ deferred = deferred.addErrback(resetCursor);
+ deferred = deferred.addCallback(resetCursor);
+ return deferred;
+}
--- a/web/data/cubicweb.calendar.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.calendar.js Wed Jul 20 18:21:47 2011 +0200
@@ -15,9 +15,9 @@
/**
* .. class:: Calendar
*
- * Calendar (graphical) widget
+ * Calendar (graphical) widget
*
- * public methods are :
+ * public methods are :
*
* __init__ :
* :attr:`containerId`: the DOM node's ID where the calendar will be displayed
@@ -74,7 +74,7 @@
/**
* .. function:: Calendar._uppercaseFirst(s)
*
- * utility function (the only use for now is inside the calendar)
+ * utility function (the only use for now is inside the calendar)
*/
this._uppercaseFirst = function(s) {
return s.charAt(0).toUpperCase();
@@ -83,7 +83,7 @@
/**
* .. function:: Calendar._domForRows(rows)
*
- * accepts the cells data and builds the corresponding TR nodes
+ * accepts the cells data and builds the corresponding TR nodes
*
* * `rows`, a list of list of couples (daynum, cssprops)
*/
@@ -98,7 +98,7 @@
/**
* .. function:: Calendar._headdisplay(row)
*
- * builds the calendar headers
+ * builds the calendar headers
*/
this._headdisplay = function(row) {
if (_CAL_HEADER) {
@@ -224,13 +224,17 @@
this.hide); // connect(inputId, 'onfocus', this, 'hide');
};
-// keep track of each calendar created
+/**
+ * .. data:: Calendar.REGISTRY
+ *
+ * keep track of each calendar created
+ */
Calendar.REGISTRY = {};
/**
* .. function:: toggleCalendar(containerId, inputId, year, month)
*
- * popup / hide calendar associated to `containerId`
+ * popup / hide calendar associated to `containerId`
*/
function toggleCalendar(containerId, inputId, year, month) {
var cal = Calendar.REGISTRY[containerId];
@@ -251,7 +255,7 @@
/**
* .. function:: toggleNextMonth(containerId)
*
- * ask for next month to calendar displayed in `containerId`
+ * ask for next month to calendar displayed in `containerId`
*/
function toggleNextMonth(containerId) {
var cal = Calendar.REGISTRY[containerId];
@@ -261,7 +265,7 @@
/**
* .. function:: togglePreviousMonth(containerId)
*
- * ask for previous month to calendar displayed in `containerId`
+ * ask for previous month to calendar displayed in `containerId`
*/
function togglePreviousMonth(containerId) {
var cal = Calendar.REGISTRY[containerId];
@@ -271,7 +275,7 @@
/**
* .. function:: dateSelected(cell, containerId)
*
- * Callback called when the user clicked on a cell in the popup calendar
+ * callback called when the user clicked on a cell in the popup calendar
*/
function dateSelected(cell, containerId) {
var cal = Calendar.REGISTRY[containerId];
--- a/web/data/cubicweb.css Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.css Wed Jul 20 18:21:47 2011 +0200
@@ -993,23 +993,49 @@
background-image: none;
}
-/* ui.tabs.css */
-ul.ui-tabs-nav,
-div.ui-tabs-panel {
- font-family: %(defaultFontFamily)s;
- font-size: %(defaultSize)s;
+/* jquery-ui tabs */
+
+div.ui-tabs.ui-widget-content {
+ background:none;
+ border:none;
+ color:inherit;
+}
+
+div.ui-tabs ul.ui-tabs-nav {
+ padding-left: 0.5em;
+}
+
+div.ui-tabs ul.ui-tabs-nav a {
+ color:#27537A;
+ padding: 0.3em 0.6em;
+}
+
+div.ui-tabs ul.ui-tabs-nav li.ui-tabs-selected a {
+ color:black;
}
-div.ui-tabs-panel {
- border-top:1px solid #b6b6b6;
+div.ui-tabs ul.ui-tabs-nav li.ui-state-hover {
+ background:none;
+}
+
+div.ui-tabs .ui-widget-header {
+ background:none;
+ border:none;
}
-ul.ui-tabs-nav a {
- color: #3d3d3d;
+div.ui-tabs .ui-widget-header li {
+ border-color:#333333;
}
-ul.ui-tabs-nav a:hover {
- color: #000;
+div.ui-tabs .ui-tabs-panel {
+ border-top:1px solid #97A5B0;
+ padding-left:0.5em;
+ color:inherit;
+}
+
+div.ui-tabs .ui-tabs-nav, div.ui-tabs .ui-tabs-panel {
+ font-family: %(defaultFontFamily)s;
+ font-size: %(defaultSize)s;
}
img.ui-datepicker-trigger {
--- a/web/data/cubicweb.edition.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.edition.js Wed Jul 20 18:21:47 2011 +0200
@@ -583,6 +583,7 @@
* around the corresponding input fields.
*/
function validateForm(formid, action, onsuccess, onfailure) {
+ freezeFormButtons(formid);
try {
var zipped = cw.utils.formContents(formid);
var args = ajaxFuncArgs('validate_form', null, action, zipped[0], zipped[1]);
--- a/web/data/cubicweb.facets.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.facets.js Wed Jul 20 18:21:47 2011 +0200
@@ -133,11 +133,20 @@
// may changes and we must send its value when the callback is
// called, not when the page is initialized
var facetargs = form.attr('cubicweb:facetargs');
- if (facetargs !== undefined) {
+ if (facetargs != undefined && !form.attr('cubicweb:initialized')) {
+ form.attr('cubicweb:initialized', '1');
+ var jsfacetargs = cw.evalJSON(form.attr('cubicweb:facetargs'));
form.submit(function() {
- buildRQL.apply(null, cw.evalJSON(form.attr('cubicweb:facetargs')));
+ buildRQL.apply(null, jsfacetargs);
return false;
});
+ var divid = jsfacetargs[0];
+ if (jQuery('#'+divid).length) {
+ var $loadingDiv = $(DIV({id:'facetLoading'},
+ facetLoadingMsg));
+ $loadingDiv.corner();
+ $(jQuery('#'+divid).get(0).parentNode).append($loadingDiv);
+ }
form.find('div.facet').each(function() {
var facet = jQuery(this);
facet.find('div.facetCheckBox').each(function(i) {
@@ -188,7 +197,7 @@
var $img = jQuery(this).find('img');
$img.attr('src', SELECTED_IMG).attr('alt', (_('selected')));
}
- buildRQL.apply(null, cw.evalJSON(form.attr('cubicweb:facetargs')));
+ buildRQL.apply(null, jsfacetargs);
facet.find('.facetBody').animate({
scrollTop: 0
},
@@ -197,7 +206,7 @@
facet.find('select.facetOperator').change(function() {
var nbselected = facet.find('div.facetValueSelected').length;
if (nbselected >= 2) {
- buildRQL.apply(null, cw.evalJSON(form.attr('cubicweb:facetargs')));
+ buildRQL.apply(null, jsfacetargs);
}
});
facet.find('div.facetTitle').click(function() {
--- a/web/data/cubicweb.flot.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.flot.js Wed Jul 20 18:21:47 2011 +0200
@@ -13,16 +13,25 @@
var previousPoint = null;
function onPlotHover(event, pos, item) {
+ var $fig = $(event.target);
if (item) {
if (previousPoint != item.datapoint) {
previousPoint = item.datapoint;
$("#tooltip").remove();
var x = item.datapoint[0].toFixed(2),
- y = item.datapoint[1].toFixed(2);
- if (item.datapoint.length == 3) {
- x = new Date(item.datapoint[2]);
- x = x.toLocaleDateString() + ' ' + x.toLocaleTimeString();
+ y = item.datapoint[1].toFixed(2);
+ if ($fig.data('mode') == 'time') {
+ x = new Date(item.datapoint[0]);
+ var dateformat = $fig.data('dateformat');
+ if (dateformat) {
+ x = x.strftime(dateformat);
+ } else {
+ x = x.toLocaleDateString() + ' ' + x.toLocaleTimeString();
+ }
} else if (item.datapoint.length == 4) {
+ // NOTE: this has no chance to work with jquery flot >= 0.6 because
+ // jquery flot normalizes datapoints and only keeps 2 columns. Either
+ // use processRawData hook or use the 'dateformat' option.
x = new Date(item.datapoint[2]);
x = x.strftime(item.datapoint[3]);
}
--- a/web/data/cubicweb.lazy.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-
--- a/web/data/cubicweb.old.css Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.old.css Wed Jul 20 18:21:47 2011 +0200
@@ -976,3 +976,42 @@
/* remove background image (orange bullet) for autocomplete suggestions */
background-image: none;
}
+
+div.ui-tabs.ui-widget-content {
+ background:none;
+ border:none;
+ color:inherit;
+}
+
+div.ui-tabs ul.ui-tabs-nav {
+ padding-left: 0.5em;
+}
+
+div.ui-tabs ul.ui-tabs-nav a {
+ color:#27537A;
+ padding: 0.3em 0.6em;
+ outline:0;
+}
+
+div.ui-tabs ul.ui-tabs-nav li.ui-tabs-selected a {
+ color:black;
+}
+
+div.ui-tabs ul.ui-tabs-nav li.ui-state-hover, div.ui-tabs ul.ui-tabs-nav li.ui-state-focus {
+ background:white;
+}
+
+div.ui-tabs .ui-widget-header {
+ background:none;
+ border:none;
+}
+
+div.ui-tabs .ui-widget-header li {
+ border-color:#333333;
+}
+
+div.ui-tabs .ui-tabs-panel {
+ border-top:1px solid #97A5B0;
+ padding-left:0.5em;
+ color:inherit;
+}
--- a/web/data/cubicweb.reledit.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.reledit.js Wed Jul 20 18:21:47 2011 +0200
@@ -18,6 +18,7 @@
cleanupAfterCancel: function (divid) {
jQuery('#appMsg').hide();
jQuery('div.errorMessage').remove();
+ // plus re-set inline style ?
jQuery('#' + divid).show();
jQuery('#' + divid + '-value').show();
jQuery('#' + divid + '-form').hide();
@@ -63,9 +64,9 @@
* @param reload: boolean to reload page if true (when changing URL dependant data)
* @param default_value : value if the field is empty
*/
- loadInlineEditionForm: function(formid, eid, rtype, role, divid, reload, vid) {
+ loadInlineEditionForm: function(formid, eid, rtype, role, divid, reload, vid, action) {
var args = {fname: 'reledit_form', rtype: rtype, role: role,
- pageid: pageid,
+ pageid: pageid, action: action,
eid: eid, divid: divid, formid: formid,
reload: reload, vid: vid};
var d = jQuery('#'+divid+'-reledit').loadxhtml(JSON_BASE_URL, args, 'post');
--- a/web/data/cubicweb.tabs.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-
--- a/web/data/cubicweb.timeline-bundle.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/cubicweb.timeline-bundle.js Wed Jul 20 18:21:47 2011 +0200
@@ -1,10 +1,15 @@
+/**
+ * This file contains timeline utilities
+ * :organization: Logilab
+ */
+
var SimileAjax_urlPrefix = baseuri() + 'data/';
var Timeline_urlPrefix = baseuri() + 'data/';
/*
* Simile Ajax API
*
- * Include this file in your HTML file as follows:
+ * Include this file in your HTML file as follows::
*
* <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
*
--- a/web/data/excanvas.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/excanvas.js Wed Jul 20 18:21:47 2011 +0200
@@ -1,924 +1,30 @@
-// Copyright 2006 Google 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.
-
-
-// Known Issues:
-//
-// * Patterns are not implemented.
-// * Radial gradient are not implemented. The VML version of these look very
-// different from the canvas one.
-// * Clipping paths are not implemented.
-// * Coordsize. The width and height attribute have higher priority than the
-// width and height style values which isn't correct.
-// * Painting mode isn't implemented.
-// * Canvas width/height should is using content-box by default. IE in
-// Quirks mode will draw the canvas using border-box. Either change your
-// doctype to HTML5
-// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
-// or use Box Sizing Behavior from WebFX
-// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
-// * Non uniform scaling does not correctly scale strokes.
-// * Optimize. There is always room for speed improvements.
-
-// Only add this code if we do not already have a canvas implementation
-if (!document.createElement('canvas').getContext) {
-
-(function() {
-
- // alias some functions to make (compiled) code shorter
- var m = Math;
- var mr = m.round;
- var ms = m.sin;
- var mc = m.cos;
- var abs = m.abs;
- var sqrt = m.sqrt;
-
- // this is used for sub pixel precision
- var Z = 10;
- var Z2 = Z / 2;
-
- /**
- * This funtion is assigned to the <canvas> elements as element.getContext().
- * @this {HTMLElement}
- * @return {CanvasRenderingContext2D_}
- */
- function getContext() {
- return this.context_ ||
- (this.context_ = new CanvasRenderingContext2D_(this));
- }
-
- var slice = Array.prototype.slice;
-
- /**
- * Binds a function to an object. The returned function will always use the
- * passed in {@code obj} as {@code this}.
- *
- * Example:
- *
- * g = bind(f, obj, a, b)
- * g(c, d) // will do f.call(obj, a, b, c, d)
- *
- * @param {Function} f The function to bind the object to
- * @param {Object} obj The object that should act as this when the function
- * is called
- * @param {*} var_args Rest arguments that will be used as the initial
- * arguments when the function is called
- * @return {Function} A new function that has bound this
- */
- function bind(f, obj, var_args) {
- var a = slice.call(arguments, 2);
- return function() {
- return f.apply(obj, a.concat(slice.call(arguments)));
- };
- }
-
- var G_vmlCanvasManager_ = {
- init: function(opt_doc) {
- if (/MSIE/.test(navigator.userAgent) && !window.opera) {
- var doc = opt_doc || document;
- // Create a dummy element so that IE will allow canvas elements to be
- // recognized.
- doc.createElement('canvas');
- doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
- }
- },
-
- init_: function(doc) {
- // create xmlns
- if (!doc.namespaces['g_vml_']) {
- doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
- '#default#VML');
-
- }
- if (!doc.namespaces['g_o_']) {
- doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
- '#default#VML');
- }
-
- // Setup default CSS. Only add one style sheet per document
- if (!doc.styleSheets['ex_canvas_']) {
- var ss = doc.createStyleSheet();
- ss.owningElement.id = 'ex_canvas_';
- ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
- // default size is 300x150 in Gecko and Opera
- 'text-align:left;width:300px;height:150px}' +
- 'g_vml_\\:*{behavior:url(#default#VML)}' +
- 'g_o_\\:*{behavior:url(#default#VML)}';
-
- }
-
- // find all canvas elements
- var els = doc.getElementsByTagName('canvas');
- for (var i = 0; i < els.length; i++) {
- this.initElement(els[i]);
- }
- },
-
- /**
- * Public initializes a canvas element so that it can be used as canvas
- * element from now on. This is called automatically before the page is
- * loaded but if you are creating elements using createElement you need to
- * make sure this is called on the element.
- * @param {HTMLElement} el The canvas element to initialize.
- * @return {HTMLElement} the element that was created.
- */
- initElement: function(el) {
- if (!el.getContext) {
-
- el.getContext = getContext;
-
- // Remove fallback content. There is no way to hide text nodes so we
- // just remove all childNodes. We could hide all elements and remove
- // text nodes but who really cares about the fallback content.
- el.innerHTML = '';
-
- // do not use inline function because that will leak memory
- el.attachEvent('onpropertychange', onPropertyChange);
- el.attachEvent('onresize', onResize);
-
- var attrs = el.attributes;
- if (attrs.width && attrs.width.specified) {
- // TODO: use runtimeStyle and coordsize
- // el.getContext().setWidth_(attrs.width.nodeValue);
- el.style.width = attrs.width.nodeValue + 'px';
- } else {
- el.width = el.clientWidth;
- }
- if (attrs.height && attrs.height.specified) {
- // TODO: use runtimeStyle and coordsize
- // el.getContext().setHeight_(attrs.height.nodeValue);
- el.style.height = attrs.height.nodeValue + 'px';
- } else {
- el.height = el.clientHeight;
- }
- //el.getContext().setCoordsize_()
- }
- return el;
- }
- };
-
- function onPropertyChange(e) {
- var el = e.srcElement;
-
- switch (e.propertyName) {
- case 'width':
- el.style.width = el.attributes.width.nodeValue + 'px';
- el.getContext().clearRect();
- break;
- case 'height':
- el.style.height = el.attributes.height.nodeValue + 'px';
- el.getContext().clearRect();
- break;
- }
- }
-
- function onResize(e) {
- var el = e.srcElement;
- if (el.firstChild) {
- el.firstChild.style.width = el.clientWidth + 'px';
- el.firstChild.style.height = el.clientHeight + 'px';
- }
- }
-
- G_vmlCanvasManager_.init();
-
- // precompute "00" to "FF"
- var dec2hex = [];
- for (var i = 0; i < 16; i++) {
- for (var j = 0; j < 16; j++) {
- dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
- }
- }
-
- function createMatrixIdentity() {
- return [
- [1, 0, 0],
- [0, 1, 0],
- [0, 0, 1]
- ];
- }
-
- function matrixMultiply(m1, m2) {
- var result = createMatrixIdentity();
-
- for (var x = 0; x < 3; x++) {
- for (var y = 0; y < 3; y++) {
- var sum = 0;
-
- for (var z = 0; z < 3; z++) {
- sum += m1[x][z] * m2[z][y];
- }
-
- result[x][y] = sum;
- }
- }
- return result;
- }
-
- function copyState(o1, o2) {
- o2.fillStyle = o1.fillStyle;
- o2.lineCap = o1.lineCap;
- o2.lineJoin = o1.lineJoin;
- o2.lineWidth = o1.lineWidth;
- o2.miterLimit = o1.miterLimit;
- o2.shadowBlur = o1.shadowBlur;
- o2.shadowColor = o1.shadowColor;
- o2.shadowOffsetX = o1.shadowOffsetX;
- o2.shadowOffsetY = o1.shadowOffsetY;
- o2.strokeStyle = o1.strokeStyle;
- o2.globalAlpha = o1.globalAlpha;
- o2.arcScaleX_ = o1.arcScaleX_;
- o2.arcScaleY_ = o1.arcScaleY_;
- o2.lineScale_ = o1.lineScale_;
- }
-
- function processStyle(styleString) {
- var str, alpha = 1;
-
- styleString = String(styleString);
- if (styleString.substring(0, 3) == 'rgb') {
- var start = styleString.indexOf('(', 3);
- var end = styleString.indexOf(')', start + 1);
- var guts = styleString.substring(start + 1, end).split(',');
-
- str = '#';
- for (var i = 0; i < 3; i++) {
- str += dec2hex[Number(guts[i])];
- }
-
- if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
- alpha = guts[3];
- }
- } else {
- str = styleString;
- }
-
- return {color: str, alpha: alpha};
- }
-
- function processLineCap(lineCap) {
- switch (lineCap) {
- case 'butt':
- return 'flat';
- case 'round':
- return 'round';
- case 'square':
- default:
- return 'square';
- }
- }
-
- /**
- * This class implements CanvasRenderingContext2D interface as described by
- * the WHATWG.
- * @param {HTMLElement} surfaceElement The element that the 2D context should
- * be associated with
- */
- function CanvasRenderingContext2D_(surfaceElement) {
- this.m_ = createMatrixIdentity();
-
- this.mStack_ = [];
- this.aStack_ = [];
- this.currentPath_ = [];
-
- // Canvas context properties
- this.strokeStyle = '#000';
- this.fillStyle = '#000';
-
- this.lineWidth = 1;
- this.lineJoin = 'miter';
- this.lineCap = 'butt';
- this.miterLimit = Z * 1;
- this.globalAlpha = 1;
- this.canvas = surfaceElement;
-
- var el = surfaceElement.ownerDocument.createElement('div');
- el.style.width = surfaceElement.clientWidth + 'px';
- el.style.height = surfaceElement.clientHeight + 'px';
- el.style.overflow = 'hidden';
- el.style.position = 'absolute';
- surfaceElement.appendChild(el);
-
- this.element_ = el;
- this.arcScaleX_ = 1;
- this.arcScaleY_ = 1;
- this.lineScale_ = 1;
- }
-
- var contextPrototype = CanvasRenderingContext2D_.prototype;
- contextPrototype.clearRect = function() {
- this.element_.innerHTML = '';
- };
-
- contextPrototype.beginPath = function() {
- // TODO: Branch current matrix so that save/restore has no effect
- // as per safari docs.
- this.currentPath_ = [];
- };
-
- contextPrototype.moveTo = function(aX, aY) {
- var p = this.getCoords_(aX, aY);
- this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
- this.currentX_ = p.x;
- this.currentY_ = p.y;
- };
-
- contextPrototype.lineTo = function(aX, aY) {
- var p = this.getCoords_(aX, aY);
- this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
-
- this.currentX_ = p.x;
- this.currentY_ = p.y;
- };
-
- contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
- aCP2x, aCP2y,
- aX, aY) {
- var p = this.getCoords_(aX, aY);
- var cp1 = this.getCoords_(aCP1x, aCP1y);
- var cp2 = this.getCoords_(aCP2x, aCP2y);
- bezierCurveTo(this, cp1, cp2, p);
- };
-
- // Helper function that takes the already fixed cordinates.
- function bezierCurveTo(self, cp1, cp2, p) {
- self.currentPath_.push({
- type: 'bezierCurveTo',
- cp1x: cp1.x,
- cp1y: cp1.y,
- cp2x: cp2.x,
- cp2y: cp2.y,
- x: p.x,
- y: p.y
- });
- self.currentX_ = p.x;
- self.currentY_ = p.y;
- }
-
- contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
- // the following is lifted almost directly from
- // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
-
- var cp = this.getCoords_(aCPx, aCPy);
- var p = this.getCoords_(aX, aY);
-
- var cp1 = {
- x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
- y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
- };
- var cp2 = {
- x: cp1.x + (p.x - this.currentX_) / 3.0,
- y: cp1.y + (p.y - this.currentY_) / 3.0
- };
-
- bezierCurveTo(this, cp1, cp2, p);
- };
-
- contextPrototype.arc = function(aX, aY, aRadius,
- aStartAngle, aEndAngle, aClockwise) {
- aRadius *= Z;
- var arcType = aClockwise ? 'at' : 'wa';
-
- var xStart = aX + mc(aStartAngle) * aRadius - Z2;
- var yStart = aY + ms(aStartAngle) * aRadius - Z2;
-
- var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
- var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
-
- // IE won't render arches drawn counter clockwise if xStart == xEnd.
- if (xStart == xEnd && !aClockwise) {
- xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
- // that can be represented in binary
- }
-
- var p = this.getCoords_(aX, aY);
- var pStart = this.getCoords_(xStart, yStart);
- var pEnd = this.getCoords_(xEnd, yEnd);
-
- this.currentPath_.push({type: arcType,
- x: p.x,
- y: p.y,
- radius: aRadius,
- xStart: pStart.x,
- yStart: pStart.y,
- xEnd: pEnd.x,
- yEnd: pEnd.y});
-
- };
-
- contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
- this.moveTo(aX, aY);
- this.lineTo(aX + aWidth, aY);
- this.lineTo(aX + aWidth, aY + aHeight);
- this.lineTo(aX, aY + aHeight);
- this.closePath();
- };
-
- contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
- var oldPath = this.currentPath_;
- this.beginPath();
-
- this.moveTo(aX, aY);
- this.lineTo(aX + aWidth, aY);
- this.lineTo(aX + aWidth, aY + aHeight);
- this.lineTo(aX, aY + aHeight);
- this.closePath();
- this.stroke();
-
- this.currentPath_ = oldPath;
- };
-
- contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
- var oldPath = this.currentPath_;
- this.beginPath();
-
- this.moveTo(aX, aY);
- this.lineTo(aX + aWidth, aY);
- this.lineTo(aX + aWidth, aY + aHeight);
- this.lineTo(aX, aY + aHeight);
- this.closePath();
- this.fill();
-
- this.currentPath_ = oldPath;
- };
-
- contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
- var gradient = new CanvasGradient_('gradient');
- gradient.x0_ = aX0;
- gradient.y0_ = aY0;
- gradient.x1_ = aX1;
- gradient.y1_ = aY1;
- return gradient;
- };
-
- contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
- aX1, aY1, aR1) {
- var gradient = new CanvasGradient_('gradientradial');
- gradient.x0_ = aX0;
- gradient.y0_ = aY0;
- gradient.r0_ = aR0;
- gradient.x1_ = aX1;
- gradient.y1_ = aY1;
- gradient.r1_ = aR1;
- return gradient;
- };
-
- contextPrototype.drawImage = function(image, var_args) {
- var dx, dy, dw, dh, sx, sy, sw, sh;
-
- // to find the original width we overide the width and height
- var oldRuntimeWidth = image.runtimeStyle.width;
- var oldRuntimeHeight = image.runtimeStyle.height;
- image.runtimeStyle.width = 'auto';
- image.runtimeStyle.height = 'auto';
-
- // get the original size
- var w = image.width;
- var h = image.height;
-
- // and remove overides
- image.runtimeStyle.width = oldRuntimeWidth;
- image.runtimeStyle.height = oldRuntimeHeight;
-
- if (arguments.length == 3) {
- dx = arguments[1];
- dy = arguments[2];
- sx = sy = 0;
- sw = dw = w;
- sh = dh = h;
- } else if (arguments.length == 5) {
- dx = arguments[1];
- dy = arguments[2];
- dw = arguments[3];
- dh = arguments[4];
- sx = sy = 0;
- sw = w;
- sh = h;
- } else if (arguments.length == 9) {
- sx = arguments[1];
- sy = arguments[2];
- sw = arguments[3];
- sh = arguments[4];
- dx = arguments[5];
- dy = arguments[6];
- dw = arguments[7];
- dh = arguments[8];
- } else {
- throw Error('Invalid number of arguments');
- }
-
- var d = this.getCoords_(dx, dy);
-
- var w2 = sw / 2;
- var h2 = sh / 2;
-
- var vmlStr = [];
-
- var W = 10;
- var H = 10;
-
- // For some reason that I've now forgotten, using divs didn't work
- vmlStr.push(' <g_vml_:group',
- ' coordsize="', Z * W, ',', Z * H, '"',
- ' coordorigin="0,0"' ,
- ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
-
- // If filters are necessary (rotation exists), create them
- // filters are bog-slow, so only create them if abbsolutely necessary
- // The following check doesn't account for skews (which don't exist
- // in the canvas spec (yet) anyway.
-
- if (this.m_[0][0] != 1 || this.m_[0][1]) {
- var filter = [];
-
- // Note the 12/21 reversal
- filter.push('M11=', this.m_[0][0], ',',
- 'M12=', this.m_[1][0], ',',
- 'M21=', this.m_[0][1], ',',
- 'M22=', this.m_[1][1], ',',
- 'Dx=', mr(d.x / Z), ',',
- 'Dy=', mr(d.y / Z), '');
-
- // Bounding box calculation (need to minimize displayed area so that
- // filters don't waste time on unused pixels.
- var max = d;
- var c2 = this.getCoords_(dx + dw, dy);
- var c3 = this.getCoords_(dx, dy + dh);
- var c4 = this.getCoords_(dx + dw, dy + dh);
-
- max.x = m.max(max.x, c2.x, c3.x, c4.x);
- max.y = m.max(max.y, c2.y, c3.y, c4.y);
-
- vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
- 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
- filter.join(''), ", sizingmethod='clip');")
- } else {
- vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
- }
-
- vmlStr.push(' ">' ,
- '<g_vml_:image src="', image.src, '"',
- ' style="width:', Z * dw, 'px;',
- ' height:', Z * dh, 'px;"',
- ' cropleft="', sx / w, '"',
- ' croptop="', sy / h, '"',
- ' cropright="', (w - sx - sw) / w, '"',
- ' cropbottom="', (h - sy - sh) / h, '"',
- ' />',
- '</g_vml_:group>');
-
- this.element_.insertAdjacentHTML('BeforeEnd',
- vmlStr.join(''));
- };
-
- contextPrototype.stroke = function(aFill) {
- var lineStr = [];
- var lineOpen = false;
- var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
- var color = a.color;
- var opacity = a.alpha * this.globalAlpha;
-
- var W = 10;
- var H = 10;
-
- lineStr.push('<g_vml_:shape',
- ' filled="', !!aFill, '"',
- ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
- ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
- ' stroked="', !aFill, '"',
- ' path="');
-
- var newSeq = false;
- var min = {x: null, y: null};
- var max = {x: null, y: null};
-
- for (var i = 0; i < this.currentPath_.length; i++) {
- var p = this.currentPath_[i];
- var c;
-
- switch (p.type) {
- case 'moveTo':
- c = p;
- lineStr.push(' m ', mr(p.x), ',', mr(p.y));
- break;
- case 'lineTo':
- lineStr.push(' l ', mr(p.x), ',', mr(p.y));
- break;
- case 'close':
- lineStr.push(' x ');
- p = null;
- break;
- case 'bezierCurveTo':
- lineStr.push(' c ',
- mr(p.cp1x), ',', mr(p.cp1y), ',',
- mr(p.cp2x), ',', mr(p.cp2y), ',',
- mr(p.x), ',', mr(p.y));
- break;
- case 'at':
- case 'wa':
- lineStr.push(' ', p.type, ' ',
- mr(p.x - this.arcScaleX_ * p.radius), ',',
- mr(p.y - this.arcScaleY_ * p.radius), ' ',
- mr(p.x + this.arcScaleX_ * p.radius), ',',
- mr(p.y + this.arcScaleY_ * p.radius), ' ',
- mr(p.xStart), ',', mr(p.yStart), ' ',
- mr(p.xEnd), ',', mr(p.yEnd));
- break;
- }
-
-
- // TODO: Following is broken for curves due to
- // move to proper paths.
-
- // Figure out dimensions so we can do gradient fills
- // properly
- if (p) {
- if (min.x == null || p.x < min.x) {
- min.x = p.x;
- }
- if (max.x == null || p.x > max.x) {
- max.x = p.x;
- }
- if (min.y == null || p.y < min.y) {
- min.y = p.y;
- }
- if (max.y == null || p.y > max.y) {
- max.y = p.y;
- }
- }
- }
- lineStr.push(' ">');
-
- if (!aFill) {
- var lineWidth = this.lineScale_ * this.lineWidth;
-
- // VML cannot correctly render a line if the width is less than 1px.
- // In that case, we dilute the color to make the line look thinner.
- if (lineWidth < 1) {
- opacity *= lineWidth;
- }
-
- lineStr.push(
- '<g_vml_:stroke',
- ' opacity="', opacity, '"',
- ' joinstyle="', this.lineJoin, '"',
- ' miterlimit="', this.miterLimit, '"',
- ' endcap="', processLineCap(this.lineCap), '"',
- ' weight="', lineWidth, 'px"',
- ' color="', color, '" />'
- );
- } else if (typeof this.fillStyle == 'object') {
- var fillStyle = this.fillStyle;
- var angle = 0;
- var focus = {x: 0, y: 0};
-
- // additional offset
- var shift = 0;
- // scale factor for offset
- var expansion = 1;
-
- if (fillStyle.type_ == 'gradient') {
- var x0 = fillStyle.x0_ / this.arcScaleX_;
- var y0 = fillStyle.y0_ / this.arcScaleY_;
- var x1 = fillStyle.x1_ / this.arcScaleX_;
- var y1 = fillStyle.y1_ / this.arcScaleY_;
- var p0 = this.getCoords_(x0, y0);
- var p1 = this.getCoords_(x1, y1);
- var dx = p1.x - p0.x;
- var dy = p1.y - p0.y;
- angle = Math.atan2(dx, dy) * 180 / Math.PI;
-
- // The angle should be a non-negative number.
- if (angle < 0) {
- angle += 360;
- }
-
- // Very small angles produce an unexpected result because they are
- // converted to a scientific notation string.
- if (angle < 1e-6) {
- angle = 0;
- }
- } else {
- var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
- var width = max.x - min.x;
- var height = max.y - min.y;
- focus = {
- x: (p0.x - min.x) / width,
- y: (p0.y - min.y) / height
- };
-
- width /= this.arcScaleX_ * Z;
- height /= this.arcScaleY_ * Z;
- var dimension = m.max(width, height);
- shift = 2 * fillStyle.r0_ / dimension;
- expansion = 2 * fillStyle.r1_ / dimension - shift;
- }
-
- // We need to sort the color stops in ascending order by offset,
- // otherwise IE won't interpret it correctly.
- var stops = fillStyle.colors_;
- stops.sort(function(cs1, cs2) {
- return cs1.offset - cs2.offset;
- });
-
- var length = stops.length;
- var color1 = stops[0].color;
- var color2 = stops[length - 1].color;
- var opacity1 = stops[0].alpha * this.globalAlpha;
- var opacity2 = stops[length - 1].alpha * this.globalAlpha;
-
- var colors = [];
- for (var i = 0; i < length; i++) {
- var stop = stops[i];
- colors.push(stop.offset * expansion + shift + ' ' + stop.color);
- }
-
- // When colors attribute is used, the meanings of opacity and o:opacity2
- // are reversed.
- lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
- ' method="none" focus="100%"',
- ' color="', color1, '"',
- ' color2="', color2, '"',
- ' colors="', colors.join(','), '"',
- ' opacity="', opacity2, '"',
- ' g_o_:opacity2="', opacity1, '"',
- ' angle="', angle, '"',
- ' focusposition="', focus.x, ',', focus.y, '" />');
- } else {
- lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
- '" />');
- }
-
- lineStr.push('</g_vml_:shape>');
-
- this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
- };
-
- contextPrototype.fill = function() {
- this.stroke(true);
- }
-
- contextPrototype.closePath = function() {
- this.currentPath_.push({type: 'close'});
- };
-
- /**
- * @private
- */
- contextPrototype.getCoords_ = function(aX, aY) {
- var m = this.m_;
- return {
- x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
- y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
- }
- };
-
- contextPrototype.save = function() {
- var o = {};
- copyState(this, o);
- this.aStack_.push(o);
- this.mStack_.push(this.m_);
- this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
- };
-
- contextPrototype.restore = function() {
- copyState(this.aStack_.pop(), this);
- this.m_ = this.mStack_.pop();
- };
-
- function matrixIsFinite(m) {
- for (var j = 0; j < 3; j++) {
- for (var k = 0; k < 2; k++) {
- if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
- return false;
- }
- }
- }
- return true;
- }
-
- function setM(ctx, m, updateLineScale) {
- if (!matrixIsFinite(m)) {
- return;
- }
- ctx.m_ = m;
-
- if (updateLineScale) {
- // Get the line scale.
- // Determinant of this.m_ means how much the area is enlarged by the
- // transformation. So its square root can be used as a scale factor
- // for width.
- var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
- ctx.lineScale_ = sqrt(abs(det));
- }
- }
-
- contextPrototype.translate = function(aX, aY) {
- var m1 = [
- [1, 0, 0],
- [0, 1, 0],
- [aX, aY, 1]
- ];
-
- setM(this, matrixMultiply(m1, this.m_), false);
- };
-
- contextPrototype.rotate = function(aRot) {
- var c = mc(aRot);
- var s = ms(aRot);
-
- var m1 = [
- [c, s, 0],
- [-s, c, 0],
- [0, 0, 1]
- ];
-
- setM(this, matrixMultiply(m1, this.m_), false);
- };
-
- contextPrototype.scale = function(aX, aY) {
- this.arcScaleX_ *= aX;
- this.arcScaleY_ *= aY;
- var m1 = [
- [aX, 0, 0],
- [0, aY, 0],
- [0, 0, 1]
- ];
-
- setM(this, matrixMultiply(m1, this.m_), true);
- };
-
- contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
- var m1 = [
- [m11, m12, 0],
- [m21, m22, 0],
- [dx, dy, 1]
- ];
-
- setM(this, matrixMultiply(m1, this.m_), true);
- };
-
- contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
- var m = [
- [m11, m12, 0],
- [m21, m22, 0],
- [dx, dy, 1]
- ];
-
- setM(this, m, true);
- };
-
- /******** STUBS ********/
- contextPrototype.clip = function() {
- // TODO: Implement
- };
-
- contextPrototype.arcTo = function() {
- // TODO: Implement
- };
-
- contextPrototype.createPattern = function() {
- return new CanvasPattern_;
- };
-
- // Gradient / Pattern Stubs
- function CanvasGradient_(aType) {
- this.type_ = aType;
- this.x0_ = 0;
- this.y0_ = 0;
- this.r0_ = 0;
- this.x1_ = 0;
- this.y1_ = 0;
- this.r1_ = 0;
- this.colors_ = [];
- }
-
- CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
- aColor = processStyle(aColor);
- this.colors_.push({offset: aOffset,
- color: aColor.color,
- alpha: aColor.alpha});
- };
-
- function CanvasPattern_() {}
-
- // set up externs
- G_vmlCanvasManager = G_vmlCanvasManager_;
- CanvasRenderingContext2D = CanvasRenderingContext2D_;
- CanvasGradient = CanvasGradient_;
- CanvasPattern = CanvasPattern_;
-
-})();
-
-} // if
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j<m.length;j++){this.initElement(m[j])}},initElement:function(j){if(!j.getContext){j.getContext=y;R(j.ownerDocument);j.innerHTML="";j.attachEvent("onpropertychange",x);j.attachEvent("onresize",W);var i=j.attributes;if(i.width&&i.width.specified){j.style.width=i.width.nodeValue+"px"}else{j.width=j.clientWidth}if(i.height&&i.height.specified){j.style.height=i.height.nodeValue+"px"}else{j.height=j.clientHeight}}return j},uninitElement:function(j){if(j.getContext){var i=j.getContext();delete i.element_;delete i.canvas;j.innerHTML="";j.context_=null;j.getContext=null;j.detachEvent("onpropertychange",x);j.detachEvent("onresize",W)}}};function x(j){var i=j.srcElement;switch(j.propertyName){case"width":i.getContext().clearRect();i.style.width=i.attributes.width.nodeValue+"px";i.firstChild.style.width=i.clientWidth+"px";break;case"height":i.getContext().clearRect();i.style.height=i.attributes.height.nodeValue+"px";i.firstChild.style.height=i.clientHeight+"px";break}}function W(j){var i=j.srcElement;if(i.firstChild){i.firstChild.style.width=i.clientWidth+"px";i.firstChild.style.height=i.clientHeight+"px"}}e.init();var k=[];for(var ae=0;ae<16;ae++){for(var ad=0;ad<16;ad++){k[ae*16+ad]=ae.toString(16)+ad.toString(16)}}function B(){return[[1,0,0],[0,1,0],[0,0,1]]}function J(p,m){var j=B();for(var i=0;i<3;i++){for(var ah=0;ah<3;ah++){var Z=0;for(var ag=0;ag<3;ag++){Z+=p[i][ag]*m[ag][ah]}j[i][ah]=Z}}return j}function v(j,i){i.fillStyle=j.fillStyle;i.lineCap=j.lineCap;i.lineJoin=j.lineJoin;i.lineWidth=j.lineWidth;i.miterLimit=j.miterLimit;i.shadowBlur=j.shadowBlur;i.shadowColor=j.shadowColor;i.shadowOffsetX=j.shadowOffsetX;i.shadowOffsetY=j.shadowOffsetY;i.strokeStyle=j.strokeStyle;i.globalAlpha=j.globalAlpha;i.font=j.font;i.textAlign=j.textAlign;i.textBaseline=j.textBaseline;i.arcScaleX_=j.arcScaleX_;i.arcScaleY_=j.arcScaleY_;i.lineScale_=j.lineScale_}var b={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"};function M(j){var p=j.indexOf("(",3);var i=j.indexOf(")",p+1);var m=j.substring(p+1,i).split(",");if(m.length!=4||j.charAt(3)!="a"){m[3]=1}return m}function c(i){return parseFloat(i)/100}function r(j,m,i){return Math.min(i,Math.max(m,j))}function I(ag){var i,ai,aj,ah,ak,Z;ah=parseFloat(ag[0])/360%360;if(ah<0){ah++}ak=r(c(ag[1]),0,1);Z=r(c(ag[2]),0,1);if(ak==0){i=ai=aj=Z}else{var j=Z<0.5?Z*(1+ak):Z+ak-Z*ak;var m=2*Z-j;i=a(m,j,ah+1/3);ai=a(m,j,ah);aj=a(m,j,ah-1/3)}return"#"+k[Math.floor(i*255)]+k[Math.floor(ai*255)]+k[Math.floor(aj*255)]}function a(j,i,m){if(m<0){m++}if(m>1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(aq,m){var aj,ah,al,ay,ao,am,at,aA;var ak=aq.runtimeStyle.width;var ap=aq.runtimeStyle.height;aq.runtimeStyle.width="auto";aq.runtimeStyle.height="auto";var ai=aq.width;var aw=aq.height;aq.runtimeStyle.width=ak;aq.runtimeStyle.height=ap;if(arguments.length==3){aj=arguments[1];ah=arguments[2];ao=am=0;at=al=ai;aA=ay=aw}else{if(arguments.length==5){aj=arguments[1];ah=arguments[2];al=arguments[3];ay=arguments[4];ao=am=0;at=ai;aA=aw}else{if(arguments.length==9){ao=arguments[1];am=arguments[2];at=arguments[3];aA=arguments[4];aj=arguments[5];ah=arguments[6];al=arguments[7];ay=arguments[8]}else{throw Error("Invalid number of arguments")}}}var az=V(this,aj,ah);var p=at/2;var j=aA/2;var ax=[];var i=10;var ag=10;ax.push(" <g_vml_:group",' coordsize="',d*i,",",d*ag,'"',' coordorigin="0,0"',' style="width:',i,"px;height:",ag,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]||this.m_[1][1]!=1||this.m_[1][0]){var Z=[];Z.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",n(az.x/d),",","Dy=",n(az.y/d),"");var av=az;var au=V(this,aj+al,ah);var ar=V(this,aj,ah+ay);var an=V(this,aj+al,ah+ay);av.x=ab.max(av.x,au.x,ar.x,an.x);av.y=ab.max(av.y,au.y,ar.y,an.y);ax.push("padding:0 ",n(av.x/d),"px ",n(av.y/d),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",Z.join(""),", sizingmethod='clip');")}else{ax.push("top:",n(az.y/d),"px;left:",n(az.x/d),"px;")}ax.push(' ">','<g_vml_:image src="',aq.src,'"',' style="width:',d*al,"px;"," height:",d*ay,'px"',' cropleft="',ao/ai,'"',' croptop="',am/aw,'"',' cropright="',(ai-ao-at)/ai,'"',' cropbottom="',(aw-am-aA)/aw,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",ax.join(""))};q.stroke=function(al){var aj=[];var Z=false;var m=10;var am=10;aj.push("<g_vml_:shape",' filled="',!!al,'"',' style="position:absolute;width:',m,"px;height:",am,'px;"',' coordorigin="0,0"',' coordsize="',d*m,",",d*am,'"',' stroked="',!al,'"',' path="');var an=false;var ag={x:null,y:null};var ak={x:null,y:null};for(var ah=0;ah<this.currentPath_.length;ah++){var j=this.currentPath_[ah];var ai;switch(j.type){case"moveTo":ai=j;aj.push(" m ",n(j.x),",",n(j.y));break;case"lineTo":aj.push(" l ",n(j.x),",",n(j.y));break;case"close":aj.push(" x ");j=null;break;case"bezierCurveTo":aj.push(" c ",n(j.cp1x),",",n(j.cp1y),",",n(j.cp2x),",",n(j.cp2y),",",n(j.x),",",n(j.y));break;case"at":case"wa":aj.push(" ",j.type," ",n(j.x-this.arcScaleX_*j.radius),",",n(j.y-this.arcScaleY_*j.radius)," ",n(j.x+this.arcScaleX_*j.radius),",",n(j.y+this.arcScaleY_*j.radius)," ",n(j.xStart),",",n(j.yStart)," ",n(j.xEnd),",",n(j.yEnd));break}if(j){if(ag.x==null||j.x<ag.x){ag.x=j.x}if(ak.x==null||j.x>ak.x){ak.x=j.x}if(ag.y==null||j.y<ag.y){ag.y=j.y}if(ak.y==null||j.y>ak.y){ak.y=j.y}}}aj.push(' ">');if(!al){w(this,aj)}else{G(this,aj,ag,ak)}aj.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",aj.join(""))};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("<g_vml_:stroke",' opacity="',Z,'"',' joinstyle="',m.lineJoin,'"',' miterlimit="',m.miterLimit,'"',' endcap="',S(m.lineCap),'"',' weight="',i,'px"',' color="',p,'" />')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH<ap;aH++){var ao=av[aH];aE.push(ao.offset*am+ax+" "+ao.color)}ai.push('<g_vml_:fill type="',aj.type_,'"',' method="none" focus="100%"',' color="',au,'"',' color2="',at,'"',' colors="',aE.join(","),'"',' opacity="',ay,'"',' g_o_:opacity2="',az,'"',' angle="',an,'"',' focusposition="',aF.x,",",aF.y,'" />')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("<g_vml_:fill",' position="',ah/j*aB*aB,",",aC/p*aA*aA,'"',' type="tile"',' src="',aj.src_,'" />')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('<g_vml_:fill color="',aw,'" opacity="',aG,'" />')}}}q.fill=function(){this.stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/2.25;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('<g_vml_:line from="',-j,' 0" to="',ar,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!ai,'" stroked="',!!ai,'" style="position:absolute;width:1px;height:1px;">');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('<g_vml_:skew on="t" matrix="',an,'" ',' offset="',al,'" origin="',j,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',af(am),'" style="v-text-align:',Z,";font:",af(p),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error;X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()};
\ No newline at end of file
--- a/web/data/jquery.corner.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/jquery.corner.js Wed Jul 20 18:21:47 2011 +0200
@@ -1,178 +1,247 @@
-/*
- * jQuery corner plugin
- *
- * version 1.92 (12/18/2007)
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- */
-
-/**
- * The corner() method provides a simple way of styling DOM elements.
- *
- * corner() takes a single string argument: $().corner("effect corners width")
- *
- * effect: The name of the effect to apply, such as round or bevel.
- * If you don't specify an effect, rounding is used.
- *
- * corners: The corners can be one or more of top, bottom, tr, tl, br, or bl.
- * By default, all four corners are adorned.
- *
- * width: The width specifies the width of the effect; in the case of rounded corners this
- * will be the radius of the width.
- * Specify this value using the px suffix such as 10px, and yes it must be pixels.
- *
- * For more details see: http://methvin.com/jquery/jq-corner.html
- * For a full demo see: http://malsup.com/jquery/corner/
- *
- *
- * @example $('.adorn').corner();
- * @desc Create round, 10px corners
- *
- * @example $('.adorn').corner("25px");
- * @desc Create round, 25px corners
- *
- * @example $('.adorn').corner("notch bottom");
- * @desc Create notched, 10px corners on bottom only
- *
- * @example $('.adorn').corner("tr dog 25px");
- * @desc Create dogeared, 25px corner on the top-right corner only
- *
- * @example $('.adorn').corner("round 8px").parent().css('padding', '4px').corner("round 10px");
- * @desc Create a rounded border effect by styling both the element and its parent
- *
- * @name corner
- * @type jQuery
- * @param String options Options which control the corner style
- * @cat Plugins/Corner
- * @return jQuery
- * @author Dave Methvin (dave.methvin@gmail.com)
- * @author Mike Alsup (malsup@gmail.com)
- */
-(function($) {
-
-$.fn.corner = function(o) {
- var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent);
- function sz(el, p) { return parseInt($.css(el,p))||0; };
- function hex2(s) {
- var s = parseInt(s).toString(16);
- return ( s.length < 2 ) ? '0'+s : s;
- };
- function gpc(node) {
- for ( ; node && node.nodeName.toLowerCase() != 'html'; node = node.parentNode ) {
- var v = $.css(node,'backgroundColor');
- if ( v.indexOf('rgb') >= 0 ) {
- if ($.browser.safari && v == 'rgba(0, 0, 0, 0)')
- continue;
- var rgb = v.match(/\d+/g);
- return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
- }
- if ( v && v != 'transparent' )
- return v;
- }
- return '#ffffff';
- };
- function getW(i) {
- switch(fx) {
- case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
- case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
- case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width))));
- case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
- case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
- case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
- case 'curl': return Math.round(width*(Math.atan(i)));
- case 'tear': return Math.round(width*(Math.cos(i)));
- case 'wicked': return Math.round(width*(Math.tan(i)));
- case 'long': return Math.round(width*(Math.sqrt(i)));
- case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
- case 'dog': return (i&1) ? (i+1) : width;
- case 'dog2': return (i&2) ? (i+1) : width;
- case 'dog3': return (i&3) ? (i+1) : width;
- case 'fray': return (i%2)*width;
- case 'notch': return width;
- case 'bevel': return i+1;
- }
- };
- o = (o||"").toLowerCase();
- var keep = /keep/.test(o); // keep borders?
- var cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]); // corner color
- var sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]); // strip color
- var width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10; // corner width
- var re = /round|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dog/;
- var fx = ((o.match(re)||['round'])[0]);
- var edges = { T:0, B:1 };
- var opts = {
- TL: /top|tl/.test(o), TR: /top|tr/.test(o),
- BL: /bottom|bl/.test(o), BR: /bottom|br/.test(o)
- };
- if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
- opts = { TL:1, TR:1, BL:1, BR:1 };
- var strip = document.createElement('div');
- strip.style.overflow = 'hidden';
- strip.style.height = '1px';
- strip.style.backgroundColor = sc || 'transparent';
- strip.style.borderStyle = 'solid';
- return this.each(function(index){
- var pad = {
- T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
- B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
- };
-
- if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE
- if (!keep) this.style.border = 'none';
- strip.style.borderColor = cc || gpc(this.parentNode);
- var cssHeight = $.curCSS(this, 'height');
-
- for (var j in edges) {
- var bot = edges[j];
- // only add stips if needed
- if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
- strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
- var d = document.createElement('div');
- $(d).addClass('jquery-corner');
- var ds = d.style;
-
- bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
-
- if (bot && cssHeight != 'auto') {
- if ($.css(this,'position') == 'static')
- this.style.position = 'relative';
- ds.position = 'absolute';
- ds.bottom = ds.left = ds.padding = ds.margin = '0';
- if (($.browser.msie) && ($.browser.version < 8.0))
- ds.setExpression('width', 'this.parentNode.offsetWidth');
- else
- ds.width = '100%';
- }
- else if (!bot && $.browser.msie) {
- if ($.css(this,'position') == 'static')
- this.style.position = 'relative';
- ds.position = 'absolute';
- ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
-
- // fix ie6 problem when blocked element has a border width
- var bw = 0;
- if (ie6 || !$.boxModel)
- bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
- ie6 ? ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"') : ds.width = '100%';
- }
- else {
- ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
- (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
- }
-
- for (var i=0; i < width; i++) {
- var w = Math.max(0,getW(i));
- var e = strip.cloneNode(false);
- e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
- bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
- }
- }
- }
- });
-};
-
-$.fn.uncorner = function(o) { return $('.jquery-corner', this).remove(); };
-
-})(jQuery);
+/*!
+ * jQuery corner plugin: simple corner rounding
+ * Examples and documentation at: http://jquery.malsup.com/corner/
+ * version 2.11 (15-JUN-2010)
+ * Requires jQuery v1.3.2 or later
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ * Authors: Dave Methvin and Mike Alsup
+ */
+
+/**
+ * corner() takes a single string argument: $('#myDiv').corner("effect corners width")
+ *
+ * effect: name of the effect to apply, such as round, bevel, notch, bite, etc (default is round).
+ * corners: one or more of: top, bottom, tr, tl, br, or bl. (default is all corners)
+ * width: width of the effect; in the case of rounded corners this is the radius.
+ * specify this value using the px suffix such as 10px (yes, it must be pixels).
+ */
+;(function($) {
+
+var style = document.createElement('div').style,
+ moz = style['MozBorderRadius'] !== undefined,
+ webkit = style['WebkitBorderRadius'] !== undefined,
+ radius = style['borderRadius'] !== undefined || style['BorderRadius'] !== undefined,
+ mode = document.documentMode || 0,
+ noBottomFold = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8),
+
+ expr = $.browser.msie && (function() {
+ var div = document.createElement('div');
+ try { div.style.setExpression('width','0+0'); div.style.removeExpression('width'); }
+ catch(e) { return false; }
+ return true;
+ })();
+
+$.support = $.support || {};
+$.support.borderRadius = moz || webkit || radius; // so you can do: if (!$.support.borderRadius) $('#myDiv').corner();
+
+function sz(el, p) {
+ return parseInt($.css(el,p))||0;
+};
+function hex2(s) {
+ var s = parseInt(s).toString(16);
+ return ( s.length < 2 ) ? '0'+s : s;
+};
+function gpc(node) {
+ while(node) {
+ var v = $.css(node,'backgroundColor'), rgb;
+ if (v && v != 'transparent' && v != 'rgba(0, 0, 0, 0)') {
+ if (v.indexOf('rgb') >= 0) {
+ rgb = v.match(/\d+/g);
+ return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
+ }
+ return v;
+ }
+ if (node.nodeName.toLowerCase() == 'html')
+ break;
+ node = node.parentNode; // keep walking if transparent
+ }
+ return '#ffffff';
+};
+
+function getWidth(fx, i, width) {
+ switch(fx) {
+ case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
+ case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
+ case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width))));
+ case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
+ case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
+ case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
+ case 'curl': return Math.round(width*(Math.atan(i)));
+ case 'tear': return Math.round(width*(Math.cos(i)));
+ case 'wicked': return Math.round(width*(Math.tan(i)));
+ case 'long': return Math.round(width*(Math.sqrt(i)));
+ case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
+ case 'dogfold':
+ case 'dog': return (i&1) ? (i+1) : width;
+ case 'dog2': return (i&2) ? (i+1) : width;
+ case 'dog3': return (i&3) ? (i+1) : width;
+ case 'fray': return (i%2)*width;
+ case 'notch': return width;
+ case 'bevelfold':
+ case 'bevel': return i+1;
+ }
+};
+
+$.fn.corner = function(options) {
+ // in 1.3+ we can fix mistakes with the ready state
+ if (this.length == 0) {
+ if (!$.isReady && this.selector) {
+ var s = this.selector, c = this.context;
+ $(function() {
+ $(s,c).corner(options);
+ });
+ }
+ return this;
+ }
+
+ return this.each(function(index){
+ var $this = $(this),
+ // meta values override options
+ o = [$this.attr($.fn.corner.defaults.metaAttr) || '', options || ''].join(' ').toLowerCase(),
+ keep = /keep/.test(o), // keep borders?
+ cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]), // corner color
+ sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]), // strip color
+ width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10, // corner width
+ re = /round|bevelfold|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dogfold|dog/,
+ fx = ((o.match(re)||['round'])[0]),
+ fold = /dogfold|bevelfold/.test(o),
+ edges = { T:0, B:1 },
+ opts = {
+ TL: /top|tl|left/.test(o), TR: /top|tr|right/.test(o),
+ BL: /bottom|bl|left/.test(o), BR: /bottom|br|right/.test(o)
+ },
+ // vars used in func later
+ strip, pad, cssHeight, j, bot, d, ds, bw, i, w, e, c, common, $horz;
+
+ if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
+ opts = { TL:1, TR:1, BL:1, BR:1 };
+
+ // support native rounding
+ if ($.fn.corner.defaults.useNative && fx == 'round' && (radius || moz || webkit) && !cc && !sc) {
+ if (opts.TL)
+ $this.css(radius ? 'border-top-left-radius' : moz ? '-moz-border-radius-topleft' : '-webkit-border-top-left-radius', width + 'px');
+ if (opts.TR)
+ $this.css(radius ? 'border-top-right-radius' : moz ? '-moz-border-radius-topright' : '-webkit-border-top-right-radius', width + 'px');
+ if (opts.BL)
+ $this.css(radius ? 'border-bottom-left-radius' : moz ? '-moz-border-radius-bottomleft' : '-webkit-border-bottom-left-radius', width + 'px');
+ if (opts.BR)
+ $this.css(radius ? 'border-bottom-right-radius' : moz ? '-moz-border-radius-bottomright' : '-webkit-border-bottom-right-radius', width + 'px');
+ return;
+ }
+
+ strip = document.createElement('div');
+ $(strip).css({
+ overflow: 'hidden',
+ height: '1px',
+ minHeight: '1px',
+ fontSize: '1px',
+ backgroundColor: sc || 'transparent',
+ borderStyle: 'solid'
+ });
+
+ pad = {
+ T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
+ B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
+ };
+
+ if (typeof this.style.zoom != undefined) this.style.zoom = 1; // force 'hasLayout' in IE
+ if (!keep) this.style.border = 'none';
+ strip.style.borderColor = cc || gpc(this.parentNode);
+ cssHeight = $(this).outerHeight();
+
+ for (j in edges) {
+ bot = edges[j];
+ // only add stips if needed
+ if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
+ strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
+ d = document.createElement('div');
+ $(d).addClass('jquery-corner');
+ ds = d.style;
+
+ bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
+
+ if (bot && cssHeight != 'auto') {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.bottom = ds.left = ds.padding = ds.margin = '0';
+ if (expr)
+ ds.setExpression('width', 'this.parentNode.offsetWidth');
+ else
+ ds.width = '100%';
+ }
+ else if (!bot && $.browser.msie) {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
+
+ // fix ie6 problem when blocked element has a border width
+ if (expr) {
+ bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
+ ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"');
+ }
+ else
+ ds.width = '100%';
+ }
+ else {
+ ds.position = 'relative';
+ ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
+ (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
+ }
+
+ for (i=0; i < width; i++) {
+ w = Math.max(0,getWidth(fx,i, width));
+ e = strip.cloneNode(false);
+ e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
+ bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
+ }
+
+ if (fold && $.support.boxModel) {
+ if (bot && noBottomFold) continue;
+ for (c in opts) {
+ if (!opts[c]) continue;
+ if (bot && (c == 'TL' || c == 'TR')) continue;
+ if (!bot && (c == 'BL' || c == 'BR')) continue;
+
+ common = { position: 'absolute', border: 'none', margin: 0, padding: 0, overflow: 'hidden', backgroundColor: strip.style.borderColor };
+ $horz = $('<div/>').css(common).css({ width: width + 'px', height: '1px' });
+ switch(c) {
+ case 'TL': $horz.css({ bottom: 0, left: 0 }); break;
+ case 'TR': $horz.css({ bottom: 0, right: 0 }); break;
+ case 'BL': $horz.css({ top: 0, left: 0 }); break;
+ case 'BR': $horz.css({ top: 0, right: 0 }); break;
+ }
+ d.appendChild($horz[0]);
+
+ var $vert = $('<div/>').css(common).css({ top: 0, bottom: 0, width: '1px', height: width + 'px' });
+ switch(c) {
+ case 'TL': $vert.css({ left: width }); break;
+ case 'TR': $vert.css({ right: width }); break;
+ case 'BL': $vert.css({ left: width }); break;
+ case 'BR': $vert.css({ right: width }); break;
+ }
+ d.appendChild($vert[0]);
+ }
+ }
+ }
+ }
+ });
+};
+
+$.fn.uncorner = function() {
+ if (radius || moz || webkit)
+ this.css(radius ? 'border-radius' : moz ? '-moz-border-radius' : '-webkit-border-radius', 0);
+ $('div.jquery-corner', this).remove();
+ return this;
+};
+
+// expose options
+$.fn.corner.defaults = {
+ useNative: true, // true if plugin should attempt to use native browser support for border radius rounding
+ metaAttr: 'data-corner' // name of meta attribute to use for options
+};
+
+})(jQuery);
--- a/web/data/jquery.js Mon May 16 16:24:00 2011 +0200
+++ b/web/data/jquery.js Wed Jul 20 18:21:47 2011 +0200
@@ -13,6228 +13,142 @@
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
-(function( window, undefined ) {
-
-// 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 );
- },
-
- // Map over jQuery in case of overwrite
- _jQuery = window.jQuery,
-
- // Map over the $ in case of overwrite
- _$ = window.$,
-
- // Use the correct document accordingly with window argument (sandbox)
- document = window.document,
-
- // A central reference to the root jQuery(document)
- rootjQuery,
-
- // A simple way to check for HTML strings or ID strings
- // (both of which we optimize for)
- quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
-
- // Is it a simple selector
- isSimple = /^.[^:#\[\.,]*$/,
-
- // Check if a string has a non-whitespace character in it
- rnotwhite = /\S/,
-
- // Used for trimming whitespace
- rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
-
- // Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
-
- // Keep a UserAgent string for use with jQuery.browser
- userAgent = navigator.userAgent,
-
- // For matching the engine and version of the browser
- browserMatch,
-
- // Has the ready events already been bound?
- readyBound = false,
-
- // The functions to execute on DOM ready
- readyList = [],
-
- // The ready event handler
- DOMContentLoaded,
-
- // Save a reference to some core methods
- toString = Object.prototype.toString,
- hasOwnProperty = Object.prototype.hasOwnProperty,
- push = Array.prototype.push,
- slice = Array.prototype.slice,
- indexOf = Array.prototype.indexOf;
-
-jQuery.fn = jQuery.prototype = {
- init: function( selector, context ) {
- 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 ) {
- this.context = document;
- this[0] = document.body;
- this.selector = "body";
- this.length = 1;
- return this;
- }
-
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- // Are we dealing with HTML string or an ID?
- 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] ) {
- 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 = buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
- }
-
- return jQuery.merge( this, selector );
-
- // HANDLE: $("#id")
- } else {
- elem = document.getElementById( match[2] );
-
- if ( elem ) {
- // 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: $("TAG")
- } else if ( !context && /^\w+$/.test( selector ) ) {
- this.selector = selector;
- this.context = document;
- selector = document.getElementsByTagName( selector );
- return jQuery.merge( this, selector );
-
- // 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 jQuery( 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.4.2",
-
- // 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.slice(num)[ 0 ] : 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 = jQuery();
-
- 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();
-
- // If the DOM is already ready
- if ( jQuery.isReady ) {
- // Execute the function immediately
- fn.call( document, jQuery );
-
- // Otherwise, remember the function for later
- } else if ( readyList ) {
- // Add the function to the wait list
- readyList.push( 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 || jQuery(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() {
- // copy reference to target object
- var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
-
- // 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 object literal values or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
- var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
- : jQuery.isArray(copy) ? [] : {};
-
- // 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 ) {
- window.$ = _$;
-
- if ( deep ) {
- window.jQuery = _jQuery;
- }
-
- return jQuery;
- },
-
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // Handle when the DOM is ready
- ready: function() {
- // Make sure that the DOM is not already loaded
- if ( !jQuery.isReady ) {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready, 13 );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If there are functions bound, to execute
- if ( readyList ) {
- // Execute all of them
- var fn, i = 0;
- while ( (fn = readyList[ i++ ]) ) {
- fn.call( document, jQuery );
- }
-
- // Reset the list of functions
- readyList = null;
- }
-
- // Trigger any bound ready events
- if ( jQuery.fn.triggerHandler ) {
- jQuery( document ).triggerHandler( "ready" );
- }
- }
- },
-
- bindReady: function() {
- if ( readyBound ) {
- return;
- }
-
- readyBound = true;
-
- // Catch cases where $(document).ready() is called after the
- // browser event has already occurred.
- if ( document.readyState === "complete" ) {
- return jQuery.ready();
- }
-
- // 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 toString.call(obj) === "[object Function]";
- },
-
- isArray: function( obj ) {
- return toString.call(obj) === "[object Array]";
- },
-
- 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 || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
- return false;
- }
-
- // Not own constructor property must be Object
- if ( obj.constructor
- && !hasOwnProperty.call(obj, "constructor")
- && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
- 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 || hasOwnProperty.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 );
-
- // Make sure the incoming data is actual JSON
- // Logic borrowed from http://json.org/json2.js
- if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
- .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
-
- // Try to use the native JSON parser first
- return window.JSON && window.JSON.parse ?
- window.JSON.parse( data ) :
- (new Function("return " + data))();
-
- } else {
- jQuery.error( "Invalid JSON: " + data );
- }
- },
-
- noop: function() {},
-
- // Evalulates a script in a global context
- globalEval: function( data ) {
- if ( data && rnotwhite.test(data) ) {
- // Inspired by code by Andrea Giammarchi
- // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
- var head = document.getElementsByTagName("head")[0] || document.documentElement,
- script = document.createElement("script");
-
- script.type = "text/javascript";
-
- if ( jQuery.support.scriptEval ) {
- script.appendChild( document.createTextNode( data ) );
- } else {
- script.text = data;
- }
-
- // Use insertBefore instead of appendChild to circumvent an IE6 bug.
- // This arises when a base node is used (#2709).
- head.insertBefore( script, head.firstChild );
- head.removeChild( script );
- }
- },
-
- 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 ( var value = object[0];
- i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
- }
- }
-
- return object;
- },
-
- trim: function( text ) {
- return (text || "").replace( rtrim, "" );
- },
-
- // 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)
- if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
- push.call( ret, array );
- } else {
- jQuery.merge( ret, array );
- }
- }
-
- return ret;
- },
-
- inArray: function( 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;
- },
-
- 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 = [];
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( var i = 0, length = elems.length; i < length; i++ ) {
- if ( !inv !== !callback( elems[ i ], i ) ) {
- ret.push( elems[ i ] );
- }
- }
-
- return ret;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var ret = [], value;
-
- // Go through the array, translating each of the items to their
- // new value (or values).
- for ( var i = 0, length = elems.length; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
-
- return ret.concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- proxy: function( fn, proxy, thisObject ) {
- if ( arguments.length === 2 ) {
- if ( typeof proxy === "string" ) {
- thisObject = fn;
- fn = thisObject[ proxy ];
- proxy = undefined;
-
- } else if ( proxy && !jQuery.isFunction( proxy ) ) {
- thisObject = proxy;
- proxy = undefined;
- }
- }
-
- if ( !proxy && fn ) {
- proxy = function() {
- return fn.apply( thisObject || this, arguments );
- };
- }
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- if ( fn ) {
- proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
- }
-
- // So proxy can be declared as an argument
- return proxy;
- },
-
- // Use of jQuery.browser is frowned upon.
- // More details: http://docs.jquery.com/Utilities/jQuery.browser
- uaMatch: function( ua ) {
- ua = ua.toLowerCase();
-
- var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
- /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
- /(msie) ([\w.]+)/.exec( ua ) ||
- !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
- [];
-
- return { browser: match[1] || "", version: match[2] || "0" };
- },
-
- browser: {}
-});
-
-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;
-}
-
-if ( indexOf ) {
- jQuery.inArray = function( elem, array ) {
- return indexOf.call( array, elem );
- };
-}
-
-// 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( error ) {
- setTimeout( doScrollCheck, 1 );
- return;
- }
-
- // and execute any waiting functions
- jQuery.ready();
-}
-
-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 || "" );
- }
-
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
-}
-
-// Mutifunctional method to get and set values to a collection
-// The value/s can be optionally by executed if its a function
-function access( elems, key, value, exec, fn, pass ) {
- var length = elems.length;
-
- // Setting many attributes
- if ( typeof key === "object" ) {
- for ( var k in key ) {
- 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;
-}
-
-function now() {
- return (new Date).getTime();
-}
-(function() {
-
- jQuery.support = {};
-
- var root = document.documentElement,
- script = document.createElement("script"),
- div = document.createElement("div"),
- id = "script" + now();
-
- div.style.display = "none";
- div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-
- var all = div.getElementsByTagName("*"),
- a = div.getElementsByTagName("a")[0];
-
- // Can't get basic test support
- if ( !all || !all.length || !a ) {
- return;
- }
-
- jQuery.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 insted)
- style: /red/.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: div.getElementsByTagName("input")[0].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: document.createElement("select").appendChild( document.createElement("option") ).selected,
-
- parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
-
- // Will be defined later
- deleteExpando: true,
- checkClone: false,
- scriptEval: false,
- noCloneEvent: true,
- boxModel: null
- };
-
- script.type = "text/javascript";
- try {
- script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
- } catch(e) {}
-
- root.insertBefore( script, root.firstChild );
-
- // Make sure that the execution of code works by injecting a script
- // tag with appendChild/createTextNode
- // (IE doesn't support this, fails, and uses .text instead)
- if ( window[ id ] ) {
- jQuery.support.scriptEval = true;
- delete window[ id ];
- }
-
- // Test to see if it's possible to delete an expando from an element
- // Fails in Internet Explorer
- try {
- delete script.test;
-
- } catch(e) {
- jQuery.support.deleteExpando = false;
- }
-
- root.removeChild( script );
-
- if ( div.attachEvent && div.fireEvent ) {
- div.attachEvent("onclick", function click() {
- // Cloning a node shouldn't copy over any
- // bound event handlers (IE does this)
- jQuery.support.noCloneEvent = false;
- div.detachEvent("onclick", click);
- });
- div.cloneNode(true).fireEvent("onclick");
- }
-
- div = document.createElement("div");
- div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
-
- var fragment = document.createDocumentFragment();
- fragment.appendChild( div.firstChild );
-
- // WebKit doesn't clone checked state correctly in fragments
- jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
-
- // Figure out if the W3C box model works as expected
- // document.body must exist before we can do this
- jQuery(function() {
- var div = document.createElement("div");
- div.style.width = div.style.paddingLeft = "1px";
-
- document.body.appendChild( div );
- jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
- document.body.removeChild( div ).style.display = 'none';
-
- div = null;
- });
-
- // Technique from Juriy Zaytsev
- // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
- var eventSupported = function( eventName ) {
- var el = document.createElement("div");
- eventName = "on" + eventName;
-
- var isSupported = (eventName in el);
- if ( !isSupported ) {
- el.setAttribute(eventName, "return;");
- isSupported = typeof el[eventName] === "function";
- }
- el = null;
-
- return isSupported;
- };
-
- jQuery.support.submitBubbles = eventSupported("submit");
- jQuery.support.changeBubbles = eventSupported("change");
-
- // release memory in IE
- root = script = div = all = a = null;
-})();
-
-jQuery.props = {
- "for": "htmlFor",
- "class": "className",
- readonly: "readOnly",
- maxlength: "maxLength",
- cellspacing: "cellSpacing",
- rowspan: "rowSpan",
- colspan: "colSpan",
- tabindex: "tabIndex",
- usemap: "useMap",
- frameborder: "frameBorder"
-};
-var expando = "jQuery" + now(), uuid = 0, windowData = {};
-
-jQuery.extend({
- cache: {},
-
- expando:expando,
-
- // The following elements throw uncatchable exceptions if you
- // attempt to add expando properties to them.
- noData: {
- "embed": true,
- "object": true,
- "applet": true
- },
-
- data: function( elem, name, data ) {
- if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
- return;
- }
-
- elem = elem == window ?
- windowData :
- elem;
-
- var id = elem[ expando ], cache = jQuery.cache, thisCache;
-
- if ( !id && typeof name === "string" && data === undefined ) {
- return null;
- }
-
- // Compute a unique ID for the element
- if ( !id ) {
- id = ++uuid;
- }
-
- // Avoid generating a new cache unless none exists and we
- // want to manipulate it.
- if ( typeof name === "object" ) {
- elem[ expando ] = id;
- thisCache = cache[ id ] = jQuery.extend(true, {}, name);
-
- } else if ( !cache[ id ] ) {
- elem[ expando ] = id;
- cache[ id ] = {};
- }
-
- thisCache = cache[ id ];
-
- // Prevent overriding the named cache with undefined values
- if ( data !== undefined ) {
- thisCache[ name ] = data;
- }
-
- return typeof name === "string" ? thisCache[ name ] : thisCache;
- },
-
- removeData: function( elem, name ) {
- if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
- return;
- }
-
- elem = elem == window ?
- windowData :
- elem;
-
- var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
-
- // If we want to remove a specific section of the element's data
- if ( name ) {
- if ( thisCache ) {
- // Remove the section of cache data
- delete thisCache[ name ];
-
- // If we've removed all the data, remove the element's cache
- if ( jQuery.isEmptyObject(thisCache) ) {
- jQuery.removeData( elem );
- }
- }
-
- // Otherwise, we want to remove all of the element's data
- } else {
- if ( jQuery.support.deleteExpando ) {
- delete elem[ jQuery.expando ];
-
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- }
-
- // Completely remove the data cache
- delete cache[ id ];
- }
- }
-});
-
-jQuery.fn.extend({
- data: function( key, value ) {
- if ( typeof key === "undefined" && this.length ) {
- return jQuery.data( this[0] );
-
- } 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 ) {
- var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-
- if ( data === undefined && this.length ) {
- data = jQuery.data( this[0], key );
- }
- return data === undefined && parts[1] ?
- this.data( parts[0] ) :
- data;
- } else {
- return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
- jQuery.data( this, key, value );
- });
- }
- },
-
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
-});
-jQuery.extend({
- queue: function( elem, type, data ) {
- if ( !elem ) {
- return;
- }
-
- type = (type || "fx") + "queue";
- var q = jQuery.data( elem, type );
-
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( !data ) {
- return q || [];
- }
-
- if ( !q || jQuery.isArray(data) ) {
- q = jQuery.data( elem, type, jQuery.makeArray(data) );
-
- } else {
- q.push( data );
- }
-
- return q;
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ), fn = queue.shift();
-
- // 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);
- });
- }
- }
-});
-
-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( i, elem ) {
- 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", [] );
- }
-});
-var rclass = /[\n\t]/g,
- rspace = /\s+/,
- rreturn = /\r/g,
- rspecialurl = /href|src|style/,
- rtype = /(button|input)/i,
- rfocusable = /(button|input|object|select|textarea)/i,
- rclickable = /^(a|area)$/i,
- rradiocheck = /radio|checkbox/;
-
-jQuery.fn.extend({
- attr: function( name, value ) {
- return access( this, name, value, true, jQuery.attr );
- },
-
- removeAttr: function( name, fn ) {
- return this.each(function(){
- jQuery.attr( this, name, "" );
- if ( this.nodeType === 1 ) {
- this.removeAttribute( name );
- }
- });
- },
-
- addClass: function( value ) {
- if ( jQuery.isFunction(value) ) {
- return this.each(function(i) {
- var self = jQuery(this);
- self.addClass( value.call(this, i, self.attr("class")) );
- });
- }
-
- if ( value && typeof value === "string" ) {
- var classNames = (value || "").split( rspace );
-
- for ( var i = 0, l = this.length; i < l; i++ ) {
- var elem = this[i];
-
- if ( elem.nodeType === 1 ) {
- if ( !elem.className ) {
- elem.className = value;
-
- } else {
- var className = " " + elem.className + " ", setClass = elem.className;
- for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
- if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
- setClass += " " + classNames[c];
- }
- }
- elem.className = jQuery.trim( setClass );
- }
- }
- }
- }
-
- return this;
- },
-
- removeClass: function( value ) {
- if ( jQuery.isFunction(value) ) {
- return this.each(function(i) {
- var self = jQuery(this);
- self.removeClass( value.call(this, i, self.attr("class")) );
- });
- }
-
- if ( (value && typeof value === "string") || value === undefined ) {
- var classNames = (value || "").split(rspace);
-
- for ( var i = 0, l = this.length; i < l; i++ ) {
- var elem = this[i];
-
- if ( elem.nodeType === 1 && elem.className ) {
- if ( value ) {
- var className = (" " + elem.className + " ").replace(rclass, " ");
- for ( var 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) {
- var self = jQuery(this);
- self.toggleClass( value.call(this, i, self.attr("class"), 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].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
- return true;
- }
- }
-
- return false;
- },
-
- val: function( value ) {
- if ( value === undefined ) {
- var elem = this[0];
-
- if ( elem ) {
- if ( jQuery.nodeName( elem, "option" ) ) {
- return (elem.attributes.value || {}).specified ? elem.value : elem.text;
- }
-
- // We need to handle select boxes special
- if ( jQuery.nodeName( elem, "select" ) ) {
- var 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 ];
-
- if ( option.selected ) {
- // Get the specifc 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 );
- }
- }
-
- return values;
- }
-
- // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
- if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
- return elem.getAttribute("value") === null ? "on" : elem.value;
- }
-
-
- // Everything else, we just grab the value
- return (elem.value || "").replace(rreturn, "");
-
- }
-
- return undefined;
- }
-
- var isFunction = jQuery.isFunction(value);
-
- return this.each(function(i) {
- var self = jQuery(this), val = value;
-
- if ( this.nodeType !== 1 ) {
- return;
- }
-
- if ( isFunction ) {
- val = value.call(this, i, self.val());
- }
-
- // Typecast each time if the value is a Function and the appended
- // value is therefore different each time.
- if ( typeof val === "number" ) {
- val += "";
- }
-
- if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
- this.checked = jQuery.inArray( self.val(), val ) >= 0;
-
- } else if ( jQuery.nodeName( this, "select" ) ) {
- var values = jQuery.makeArray(val);
-
- jQuery( "option", this ).each(function() {
- this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
- });
-
- if ( !values.length ) {
- this.selectedIndex = -1;
- }
-
- } else {
- this.value = val;
- }
- });
- }
-});
-
-jQuery.extend({
- attrFn: {
- val: true,
- css: true,
- html: true,
- text: true,
- data: true,
- width: true,
- height: true,
- offset: true
- },
-
- attr: function( elem, name, value, pass ) {
- // don't set attributes on text and comment nodes
- if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
- return undefined;
- }
-
- if ( pass && name in jQuery.attrFn ) {
- return jQuery(elem)[name](value);
- }
-
- var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
- // Whether we are setting (or getting)
- set = value !== undefined;
-
- // Try to normalize/fix the name
- name = notxml && jQuery.props[ name ] || name;
-
- // Only do all the following if this is a node (faster for style)
- if ( elem.nodeType === 1 ) {
- // These attributes require special treatment
- var special = rspecialurl.test( name );
-
- // Safari mis-reports the default selected property of an option
- // Accessing the parent's selectedIndex property fixes it
- if ( name === "selected" && !jQuery.support.optSelected ) {
- var parent = elem.parentNode;
- if ( parent ) {
- parent.selectedIndex;
-
- // Make sure that it also works with optgroups, see #5701
- if ( parent.parentNode ) {
- parent.parentNode.selectedIndex;
- }
- }
- }
-
- // If applicable, access the attribute via the DOM 0 way
- if ( name in elem && notxml && !special ) {
- if ( set ) {
- // We can't allow the type property to be changed (since it causes problems in IE)
- if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
- jQuery.error( "type property can't be changed" );
- }
-
- elem[ name ] = value;
- }
-
- // browsers index elements by id/name on forms, give priority to attributes.
- if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
- return elem.getAttributeNode( name ).nodeValue;
- }
-
- // 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/
- if ( name === "tabIndex" ) {
- var attributeNode = elem.getAttributeNode( "tabIndex" );
-
- return attributeNode && attributeNode.specified ?
- attributeNode.value :
- rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
- 0 :
- undefined;
- }
-
- return elem[ name ];
- }
-
- if ( !jQuery.support.style && notxml && name === "style" ) {
- if ( set ) {
- elem.style.cssText = "" + value;
- }
-
- return elem.style.cssText;
- }
-
- if ( set ) {
- // convert the value to a string (all browsers do this but IE) see #1070
- elem.setAttribute( name, "" + value );
- }
-
- var attr = !jQuery.support.hrefNormalized && notxml && special ?
- // Some attributes require a special call on IE
- elem.getAttribute( name, 2 ) :
- elem.getAttribute( name );
-
- // Non-existent attributes return null, we normalize to undefined
- return attr === null ? undefined : attr;
- }
-
- // elem is actually elem.style ... set the style
- // Using attr for specific style information is now deprecated. Use style instead.
- return jQuery.style( elem, name, value );
- }
-});
-var rnamespaces = /\.(.*)$/,
- fcleanup = function( nm ) {
- return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
- return "\\" + ch;
- });
- };
-
-/*
- * 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;
- }
-
- // For whatever reason, IE has trouble passing the window object
- // around, causing it to be cloned in the process
- if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
- elem = window;
- }
-
- 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 = elemData.events || {},
- eventHandle = elemData.handle, eventHandle;
-
- if ( !eventHandle ) {
- elemData.handle = eventHandle = function() {
- // Handle the second event of a trigger and when
- // an event is called after a page has unloaded
- return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
- 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;
- 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 global triggering
- 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;
- }
-
- var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
- elemData = 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 ( var 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 ( var 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 ) {
- 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 );
- }
- }
- },
-
- // bubbling is internal
- trigger: function( event, data, elem /*, bubbling */ ) {
- // Event object or event type
- var type = event.type || event,
- bubbling = arguments[3];
-
- if ( !bubbling ) {
- event = typeof event === "object" ?
- // jQuery.Event object
- event[expando] ? event :
- // Object literal
- jQuery.extend( jQuery.Event(type), event ) :
- // Just the event type (string)
- jQuery.Event(type);
-
- if ( type.indexOf("!") >= 0 ) {
- event.type = type = type.slice(0, -1);
- event.exclusive = true;
- }
-
- // Handle a global trigger
- if ( !elem ) {
- // Don't bubble custom events when global (to avoid too much overhead)
- event.stopPropagation();
-
- // Only trigger if we've ever bound an event for it
- if ( jQuery.event.global[ type ] ) {
- jQuery.each( jQuery.cache, function() {
- if ( this.events && this.events[type] ) {
- jQuery.event.trigger( event, data, this.handle.elem );
- }
- });
- }
- }
-
- // Handle triggering a single element
-
- // don't do events on text and comment nodes
- if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
- return undefined;
- }
-
- // Clean up in case it is reused
- event.result = undefined;
- event.target = elem;
-
- // Clone the incoming data, if any
- data = jQuery.makeArray( data );
- data.unshift( event );
- }
-
- event.currentTarget = elem;
-
- // Trigger the event, it is assumed that "handle" is a function
- var handle = jQuery.data( elem, "handle" );
- if ( handle ) {
- handle.apply( elem, data );
- }
-
- var parent = elem.parentNode || elem.ownerDocument;
-
- // Trigger an inline bound script
- try {
- if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
- if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
- event.result = false;
- }
- }
-
- // prevent IE from throwing an error for some elements with some event types, see #3533
- } catch (e) {}
-
- if ( !event.isPropagationStopped() && parent ) {
- jQuery.event.trigger( event, data, parent, true );
-
- } else if ( !event.isDefaultPrevented() ) {
- var target = event.target, old,
- isClick = jQuery.nodeName(target, "a") && type === "click",
- special = jQuery.event.special[ type ] || {};
-
- if ( (!special._default || special._default.call( elem, event ) === false) &&
- !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
-
- try {
- if ( target[ type ] ) {
- // Make sure that we don't accidentally re-trigger the onFOO events
- old = target[ "on" + type ];
-
- if ( old ) {
- target[ "on" + type ] = null;
- }
-
- jQuery.event.triggered = true;
- target[ type ]();
- }
-
- // prevent IE from throwing an error for some elements with some event types, see #3533
- } catch (e) {}
-
- if ( old ) {
- target[ "on" + type ] = old;
- }
-
- jQuery.event.triggered = false;
- }
- }
- },
-
- handle: function( event ) {
- var all, handlers, namespaces, namespace, events;
-
- event = arguments[0] = jQuery.event.fix( event || window.event );
- event.currentTarget = this;
-
- // Namespaced event handlers
- all = event.type.indexOf(".") < 0 && !event.exclusive;
-
- if ( !all ) {
- namespaces = event.type.split(".");
- event.type = namespaces.shift();
- namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- var events = jQuery.data(this, "events"), handlers = events[ event.type ];
-
- if ( events && handlers ) {
- // Clone the handlers to prevent manipulation
- handlers = handlers.slice(0);
-
- for ( var j = 0, l = handlers.length; j < l; j++ ) {
- var handleObj = handlers[ j ];
-
- // Filter the functions by class
- if ( all || namespace.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, arguments );
-
- 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 layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-
- fix: function( event ) {
- if ( event[ 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 ) {
- event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
- }
-
- // 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 doc = document.documentElement, body = document.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 && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
- event.which = 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, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
- },
-
- remove: function( handleObj ) {
- var remove = true,
- type = handleObj.origType.replace(rnamespaces, "");
-
- jQuery.each( jQuery.data(this, "events").live || [], function() {
- if ( type === this.origType.replace(rnamespaces, "") ) {
- remove = false;
- return false;
- }
- });
-
- if ( remove ) {
- jQuery.event.remove( this, handleObj.origType, liveHandler );
- }
- }
-
- },
-
- beforeunload: {
- setup: function( data, namespaces, eventHandle ) {
- // We only want to do this special case on windows
- if ( this.setInterval ) {
- this.onbeforeunload = eventHandle;
- }
-
- return false;
- },
- teardown: function( namespaces, eventHandle ) {
- if ( this.onbeforeunload === eventHandle ) {
- this.onbeforeunload = null;
- }
- }
- }
- }
-};
-
-var removeEvent = document.removeEventListener ?
- function( elem, type, handle ) {
- elem.removeEventListener( type, handle, false );
- } :
- function( elem, type, handle ) {
- elem.detachEvent( "on" + type, handle );
- };
-
-jQuery.Event = function( src ) {
- // Allow instantiation without the 'new' keyword
- if ( !this.preventDefault ) {
- return new jQuery.Event( src );
- }
-
- // Event object
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
- // Event type
- } else {
- this.type = src;
- }
-
- // timeStamp is buggy for some events on Firefox(#3843)
- // So we won't rely on the native value
- this.timeStamp = now();
-
- // Mark it as fixed
- this[ 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)
- 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 parent = event.relatedTarget;
-
- // Firefox sometimes assigns relatedTarget a XUL element
- // which we cannot access the parentNode property of
- try {
- // Traverse up the tree
- while ( parent && parent !== this ) {
- parent = parent.parentNode;
- }
-
- if ( parent !== this ) {
- // set the correct event type
- event.type = event.data;
-
- // handle event if we actually just moused on to a non sub-element
- jQuery.event.handle.apply( this, arguments );
- }
-
- // assuming we've left the element since we most likely mousedover a xul element
- } catch(e) { }
-},
-
-// 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 ( this.nodeName.toLowerCase() !== "form" ) {
- jQuery.event.add(this, "click.specialSubmit", function( e ) {
- var elem = e.target, type = elem.type;
-
- if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
- return trigger( "submit", this, arguments );
- }
- });
-
- jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
- var elem = e.target, type = elem.type;
-
- if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
- return 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 formElems = /textarea|input|select/i,
-
- changeFilters,
-
- getVal = function( elem ) {
- var type = 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 ( elem.nodeName.toLowerCase() === "select" ) {
- val = elem.selectedIndex;
- }
-
- return val;
- },
-
- testChange = function testChange( e ) {
- var elem = e.target, data, val;
-
- if ( !formElems.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";
- return jQuery.event.trigger( e, arguments[1], elem );
- }
- };
-
- jQuery.event.special.change = {
- filters: {
- focusout: testChange,
-
- click: function( e ) {
- var elem = e.target, type = elem.type;
-
- if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
- return 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 = elem.type;
-
- if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
- (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
- type === "select-multiple" ) {
- return 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/focus[in] is not needed anymore
- 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 formElems.test( this.nodeName );
- },
-
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialChange" );
-
- return formElems.test( this.nodeName );
- }
- };
-
- changeFilters = jQuery.event.special.change.filters;
-}
-
-function trigger( type, elem, args ) {
- args[0].type = type;
- return jQuery.event.handle.apply( elem, args );
-}
-
-// Create "bubbling" focus and blur events
-if ( document.addEventListener ) {
- jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
- jQuery.event.special[ fix ] = {
- setup: function() {
- this.addEventListener( orig, handler, true );
- },
- teardown: function() {
- this.removeEventListener( orig, handler, true );
- }
- };
-
- function handler( e ) {
- e = jQuery.event.fix( e );
- e.type = fix;
- return jQuery.event.handle.call( this, e );
- }
- });
-}
-
-jQuery.each(["bind", "one"], function( i, name ) {
- jQuery.fn[ name ] = function( type, data, fn ) {
- // Handle object literals
- if ( typeof type === "object" ) {
- for ( var key in type ) {
- this[ name ](key, data, type[key], fn);
- }
- return this;
- }
-
- if ( jQuery.isFunction( data ) ) {
- fn = data;
- data = undefined;
- }
-
- var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
- jQuery( this ).unbind( event, handler );
- return fn.apply( this, arguments );
- }) : 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] ) {
- var event = jQuery.Event( type );
- event.preventDefault();
- event.stopPropagation();
- jQuery.event.trigger( event, data, this[0] );
- return event.result;
- }
- },
-
- toggle: function( fn ) {
- // Save reference to arguments for access in closure
- var args = arguments, i = 1;
-
- // link all the functions, so any of them can unbind this click handler
- while ( i < args.length ) {
- jQuery.proxy( fn, args[ i++ ] );
- }
-
- return this.click( jQuery.proxy( fn, 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;
- }));
- },
-
- 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 ( jQuery.isFunction( data ) ) {
- fn = data;
- 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 ( type === "focus" || type === "blur" ) {
- types.push( liveMap[ type ] + namespaces );
- type = type + namespaces;
-
- } else {
- type = (liveMap[ type ] || type) + namespaces;
- }
-
- if ( name === "live" ) {
- // bind live handler
- context.each(function(){
- jQuery.event.add( this, liveConvert( type, selector ),
- { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
- });
-
- } else {
- // unbind live handler
- context.unbind( liveConvert( type, selector ), fn );
- }
- }
-
- return this;
- }
-});
-
-function liveHandler( event ) {
- var stop, elems = [], selectors = [], args = arguments,
- related, match, handleObj, elem, j, i, l, data,
- events = jQuery.data( this, "events" );
-
- // Make sure we avoid non-left-click bubbling in Firefox (#3861)
- if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
- return;
- }
-
- 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++ ) {
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( match[i].selector === handleObj.selector ) {
- elem = match[i].elem;
- related = null;
-
- // Those two events require additional checking
- if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
- related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
- }
-
- if ( !related || related !== elem ) {
- elems.push({ elem: elem, handleObj: handleObj });
- }
- }
- }
- }
-
- for ( i = 0, l = elems.length; i < l; i++ ) {
- match = elems[i];
- event.currentTarget = match.elem;
- event.data = match.handleObj.data;
- event.handleObj = match.handleObj;
-
- if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
- stop = false;
- break;
- }
- }
-
- return stop;
-}
-
-function liveConvert( type, selector ) {
- return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
-}
-
-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( fn ) {
- return fn ? this.bind( name, fn ) : this.trigger( name );
- };
-
- if ( jQuery.attrFn ) {
- jQuery.attrFn[ name ] = true;
- }
-});
-
-// Prevent memory leaks in IE
-// Window isn't included so as not to unbind existing unload events
-// More info:
-// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
-if ( window.attachEvent && !window.addEventListener ) {
- window.attachEvent("onunload", function() {
- for ( var id in jQuery.cache ) {
- if ( jQuery.cache[ id ].handle ) {
- // Try/Catch is to handle iframes being unloaded, see #4280
- try {
- jQuery.event.remove( jQuery.cache[ id ].handle.elem );
- } catch(e) {}
- }
- }
- });
-}
-/*!
- * Sizzle CSS Selector Engine - v1.0
- * Copyright 2009, 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;
-
-// 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 || [];
- var origContext = context = context || document;
-
- if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
- return [];
- }
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
- soFar = selector;
-
- // Reset the position of the chunker regexp (start from head)
- while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
- soFar = m[3];
-
- parts.push( m[1] );
-
- if ( m[2] ) {
- extra = m[3];
- break;
- }
- }
-
- 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]) ) {
- var ret = Sizzle.find( parts.shift(), context, contextXML );
- context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
- }
-
- if ( context ) {
- var 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 ) {
- var 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 ( var i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
- results.push( set[i] );
- }
- }
- } else {
- for ( var 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.find = function(expr, context, isXML){
- var set, match;
-
- if ( !expr ) {
- return [];
- }
-
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var type = Expr.order[i], match;
-
- 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(/\\/g, "");
- set = Expr.find[ type ]( match, context, isXML );
- if ( set != null ) {
- expr = expr.replace( Expr.match[ type ], "" );
- break;
- }
- }
- }
- }
-
- if ( !set ) {
- set = context.getElementsByTagName("*");
- }
-
- return {set: set, expr: expr};
-};
-
-Sizzle.filter = function(expr, set, inplace, not){
- var old = expr, result = [], curLoop = set, match, anyFound,
- isXMLFilter = set && set[0] && isXML(set[0]);
-
- while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
- if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- var filter = Expr.filter[ type ], found, item, 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|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
- 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");
- }
- },
- relative: {
- "+": function(checkSet, part){
- var isPartStr = typeof part === "string",
- isTag = isPartStr && !/\W/.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 isPartStr = typeof part === "string";
-
- if ( isPartStr && !/\W/.test(part) ) {
- part = part.toLowerCase();
-
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- var parent = elem.parentNode;
- checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
- }
- }
- } else {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var 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 doneName = done++, checkFn = dirCheck;
-
- if ( typeof part === "string" && !/\W/.test(part) ) {
- var nodeCheck = part = part.toLowerCase();
- checkFn = dirNodeCheck;
- }
-
- checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
- },
- "~": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck;
-
- if ( typeof part === "string" && !/\W/.test(part) ) {
- var nodeCheck = part = part.toLowerCase();
- 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]);
- return m ? [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){
- return context.getElementsByTagName(match[1]);
- }
- },
- preFilter: {
- CLASS: function(match, curLoop, inplace, result, not, isXML){
- match = " " + match[1].replace(/\\/g, "") + " ";
-
- 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]/g, " ").indexOf(match) >= 0) ) {
- if ( !inplace ) {
- result.push( elem );
- }
- } else if ( inplace ) {
- curLoop[i] = false;
- }
- }
- }
-
- return false;
- },
- ID: function(match){
- return match[1].replace(/\\/g, "");
- },
- TAG: function(match, curLoop){
- return match[1].toLowerCase();
- },
- CHILD: function(match){
- if ( match[1] === "nth" ) {
- // 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;
- }
-
- // TODO: Move to normal caching system
- match[0] = done++;
-
- return match;
- },
- ATTR: function(match, curLoop, inplace, result, not, isXML){
- var name = match[1].replace(/\\/g, "");
-
- if ( !isXML && Expr.attrMap[name] ) {
- match[1] = Expr.attrMap[name];
- }
-
- 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
- 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){
- return "text" === elem.type;
- },
- radio: function(elem){
- return "radio" === elem.type;
- },
- checkbox: function(elem){
- return "checkbox" === elem.type;
- },
- file: function(elem){
- return "file" === elem.type;
- },
- password: function(elem){
- return "password" === elem.type;
- },
- submit: function(elem){
- return "submit" === elem.type;
- },
- image: function(elem){
- return "image" === elem.type;
- },
- reset: function(elem){
- return "reset" === elem.type;
- },
- button: function(elem){
- return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
- },
- input: function(elem){
- return /input|select|textarea|button/i.test(elem.nodeName);
- }
- },
- 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 || getText([ elem ]) || "").indexOf(match[3]) >= 0;
- } else if ( name === "not" ) {
- var not = match[3];
-
- for ( var i = 0, l = not.length; i < l; i++ ) {
- if ( not[i] === elem ) {
- return false;
- }
- }
-
- return true;
- } else {
- Sizzle.error( "Syntax error, unrecognized expression: " + 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;
-
-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, function(all, num){
- return "\\" + (num - 0 + 1);
- }));
-}
-
-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 ret = results || [];
-
- if ( toString.call(array) === "[object Array]" ) {
- Array.prototype.push.apply( ret, array );
- } else {
- if ( typeof array.length === "number" ) {
- for ( var i = 0, l = array.length; i < l; i++ ) {
- ret.push( array[i] );
- }
- } else {
- for ( var i = 0; array[i]; i++ ) {
- ret.push( array[i] );
- }
- }
- }
-
- return ret;
- };
-}
-
-var sortOrder;
-
-if ( document.documentElement.compareDocumentPosition ) {
- sortOrder = function( a, b ) {
- if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
- return a.compareDocumentPosition ? -1 : 1;
- }
-
- var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
-} else if ( "sourceIndex" in document.documentElement ) {
- sortOrder = function( a, b ) {
- if ( !a.sourceIndex || !b.sourceIndex ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
- return a.sourceIndex ? -1 : 1;
- }
-
- var ret = a.sourceIndex - b.sourceIndex;
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
-} else if ( document.createRange ) {
- sortOrder = function( a, b ) {
- if ( !a.ownerDocument || !b.ownerDocument ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
- return a.ownerDocument ? -1 : 1;
- }
-
- var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
- aRange.setStart(a, 0);
- aRange.setEnd(a, 0);
- bRange.setStart(b, 0);
- bRange.setEnd(b, 0);
- var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
-}
-
-// Utility function for retreiving the text value of an array of DOM nodes
-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;
-}
-
-// 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();
- form.innerHTML = "<a name='" + id + "'/>";
-
- // Inject it into the root element, check its status, and remove it quickly
- var root = document.documentElement;
- 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 );
- root = form = null; // release memory in IE
-})();
-
-(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);
- };
- }
-
- div = null; // release memory in IE
-})();
-
-if ( document.querySelectorAll ) {
- (function(){
- var oldSizzle = Sizzle, div = document.createElement("div");
- 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 && context.nodeType === 9 && !isXML(context) ) {
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(e){}
- }
-
- return oldSizzle(query, context, extra, seed);
- };
-
- for ( var prop in oldSizzle ) {
- Sizzle[ prop ] = oldSizzle[ prop ];
- }
-
- div = null; // release memory in IE
- })();
-}
-
-(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]);
- }
- };
-
- div = null; // release memory in IE
-})();
-
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- elem = elem[dir];
- var match = false;
-
- 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 ) {
- elem = elem[dir];
- var match = false;
-
- 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;
- }
- }
-}
-
-var contains = document.compareDocumentPosition ? function(a, b){
- return !!(a.compareDocumentPosition(b) & 16);
-} : function(a, b){
- return a !== b && (a.contains ? a.contains(b) : true);
-};
-
-var 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 tmpSet = [], later = "", match,
- 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 = getText;
-jQuery.isXMLDoc = isXML;
-jQuery.contains = contains;
-
-return;
-
-window.Sizzle = Sizzle;
-
-})();
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prevUntil|prevAll)/,
- // Note: This RegExp should be improved, or likely pulled from Sizzle
- rmultiselector = /,/,
- slice = Array.prototype.slice;
-
-// Implement the identical functionality for filter and not
-var winnow = function( elements, qualifier, keep ) {
- if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep(elements, function( elem, i ) {
- return !!qualifier.call( elem, i, elem ) === 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;
- });
-};
-
-jQuery.fn.extend({
- find: function( selector ) {
- var ret = this.pushStack( "", "find", selector ), length = 0;
-
- for ( var 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 ( var n = length; n < ret.length; n++ ) {
- for ( var 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 && jQuery.filter( selector, this ).length > 0;
- },
-
- closest: function( selectors, context ) {
- if ( jQuery.isArray( selectors ) ) {
- var ret = [], cur = this[0], match, matches = {}, selector;
-
- if ( cur && selectors.length ) {
- for ( var i = 0, l = selectors.length; i < l; i++ ) {
- selector = selectors[i];
-
- if ( !matches[selector] ) {
- matches[selector] = jQuery.expr.match.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 });
- delete matches[selector];
- }
- }
- cur = cur.parentNode;
- }
- }
-
- return ret;
- }
-
- var pos = jQuery.expr.match.POS.test( selectors ) ?
- jQuery( selectors, context || this.context ) : null;
-
- return this.map(function( i, cur ) {
- while ( cur && cur.ownerDocument && cur !== context ) {
- if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
- return cur;
- }
- cur = cur.parentNode;
- }
- return null;
- });
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
- if ( !elem || typeof elem === "string" ) {
- return jQuery.inArray( this[0],
- // If it receives a string, the selector is used
- // If it receives nothing, the siblings are used
- elem ? jQuery( elem ) : this.parent().children() );
- }
- // 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 || this.context ) :
- jQuery.makeArray( 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 );
-
- if ( !runtil.test( name ) ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- ret = jQuery.filter( selector, ret );
- }
-
- ret = this.length > 1 ? jQuery.unique( ret ) : ret;
-
- if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
-
- return this.pushStack( ret, name, slice.call(arguments).join(",") );
- };
-});
-
-jQuery.extend({
- filter: function( expr, elems, not ) {
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- return 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;
- }
-});
-var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
- rleadingWhitespace = /^\s+/,
- rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
- rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
- rtagName = /<([\w:]+)/,
- rtbody = /<tbody/i,
- rhtml = /<|&#?\w+;/,
- rnocache = /<script|<object|<embed|<option|<style/i,
- rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
- fcloseTag = function( all, front, tag ) {
- return rselfClosing.test( tag ) ?
- all :
- front + "></" + tag + ">";
- },
- 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( events ) {
- // Do the clone
- var ret = this.map(function() {
- if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
- // IE copies events bound via attachEvent when
- // using cloneNode. Calling detachEvent on the
- // clone will also remove the events from the orignal
- // In order to get around this, we use innerHTML.
- // Unfortunately, this means some modifications to
- // attributes in IE that are actually only stored
- // as properties will not be copied (such as the
- // the name attribute on an input).
- var html = this.outerHTML, ownerDocument = this.ownerDocument;
- if ( !html ) {
- var div = ownerDocument.createElement("div");
- div.appendChild( this.cloneNode(true) );
- html = div.innerHTML;
- }
-
- return jQuery.clean([html.replace(rinlinejQuery, "")
- // Handle the case in IE 8 where action=/test/> self-closes a tag
- .replace(/=([^="'>\s]+\/)>/g, '="$1">')
- .replace(rleadingWhitespace, "")], ownerDocument)[0];
- } else {
- return this.cloneNode(true);
- }
- });
-
- // Copy the events from the original to the clone
- if ( events === true ) {
- cloneCopyEvent( this, ret );
- cloneCopyEvent( this.find("*"), ret.find("*") );
- }
-
- // Return the cloned set
- return ret;
- },
-
- 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, fcloseTag);
-
- 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), old = self.html();
- self.empty().append(function(){
- return value.call( this, i, old );
- });
- });
-
- } 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.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
- }
- },
-
- detach: function( selector ) {
- return this.remove( selector, true );
- },
-
- domManip: function( args, table, callback ) {
- var results, first, value = args[0], scripts = [], fragment, parent;
-
- // 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 = 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; i < l; i++ ) {
- callback.call(
- table ?
- root(this[i], first) :
- this[i],
- i > 0 || results.cacheable || this.length > 1 ?
- fragment.cloneNode(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(orig, ret) {
- var i = 0;
-
- ret.each(function() {
- if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
- return;
- }
-
- var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
-
- if ( events ) {
- delete curData.handle;
- curData.events = {};
-
- for ( var type in events ) {
- for ( var handler in events[ type ] ) {
- jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
- }
- }
- }
- });
-}
-
-function buildFragment( args, nodes, scripts ) {
- var fragment, cacheable, cacheresults,
- doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
-
- // Only cache "small" (1/2 KB) 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 &&
- !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
-
- cacheable = true;
- cacheresults = jQuery.fragments[ args[0] ];
- if ( cacheresults ) {
- if ( 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.fn[ original ].apply( jQuery(insert[i]), elems );
- ret = ret.concat( elems );
- }
-
- return this.pushStack( ret, name, insert.selector );
- }
- };
-});
-
-jQuery.extend({
- clean: function( elems, context, fragment, scripts ) {
- 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 = [];
-
- 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" && !rhtml.test( elem ) ) {
- elem = context.createTextNode( elem );
-
- } else if ( typeof elem === "string" ) {
- // Fix "XHTML"-style tags in all browsers
- elem = elem.replace(rxhtmlTag, fcloseTag);
-
- // 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 ( var 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;
- }
-
- if ( elem.nodeType ) {
- ret.push( elem );
- } else {
- ret = jQuery.merge( ret, elem );
- }
- }
-
- if ( fragment ) {
- for ( var 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 ) {
- ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
- }
- fragment.appendChild( ret[i] );
- }
- }
- }
-
- return ret;
- },
-
- cleanData: function( elems ) {
- var data, id, cache = jQuery.cache,
- special = jQuery.event.special,
- deleteExpando = jQuery.support.deleteExpando;
-
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- id = elem[ jQuery.expando ];
-
- if ( id ) {
- data = cache[ id ];
-
- if ( data.events ) {
- for ( var type in data.events ) {
- if ( special[ type ] ) {
- jQuery.event.remove( elem, type );
-
- } else {
- removeEvent( elem, type, data.handle );
- }
- }
- }
-
- if ( deleteExpando ) {
- delete elem[ jQuery.expando ];
-
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- }
-
- delete cache[ id ];
- }
- }
- }
-});
-// exclude the following css properties to add px
-var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
- ralpha = /alpha\([^)]*\)/,
- ropacity = /opacity=([^)]*)/,
- rfloat = /float/i,
- rdashAlpha = /-([a-z])/ig,
- rupper = /([A-Z])/g,
- rnumpx = /^-?\d+(?:px)?$/i,
- rnum = /^-?\d/,
-
- cssShow = { position: "absolute", visibility: "hidden", display:"block" },
- cssWidth = [ "Left", "Right" ],
- cssHeight = [ "Top", "Bottom" ],
-
- // cache check for defaultView.getComputedStyle
- getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
- // normalize float css property
- styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
- fcamelCase = function( all, letter ) {
- return letter.toUpperCase();
- };
-
-jQuery.fn.css = function( name, value ) {
- return access( this, name, value, true, function( elem, name, value ) {
- if ( value === undefined ) {
- return jQuery.curCSS( elem, name );
- }
-
- if ( typeof value === "number" && !rexclude.test(name) ) {
- value += "px";
- }
-
- jQuery.style( elem, name, value );
- });
-};
-
-jQuery.extend({
- style: function( elem, name, value ) {
- // don't set styles on text and comment nodes
- if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
- return undefined;
- }
-
- // ignore negative width and height values #1599
- if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
- value = undefined;
- }
-
- var style = elem.style || elem, set = value !== undefined;
-
- // IE uses filters for opacity
- if ( !jQuery.support.opacity && name === "opacity" ) {
- if ( set ) {
- // IE has trouble with opacity if it does not have layout
- // Force it by setting the zoom level
- style.zoom = 1;
-
- // Set the alpha filter to set the opacity
- var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
- var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
- style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
- }
-
- return style.filter && style.filter.indexOf("opacity=") >= 0 ?
- (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
- "";
- }
-
- // Make sure we're using the right name for getting the float value
- if ( rfloat.test( name ) ) {
- name = styleFloat;
- }
-
- name = name.replace(rdashAlpha, fcamelCase);
-
- if ( set ) {
- style[ name ] = value;
- }
-
- return style[ name ];
- },
-
- css: function( elem, name, force, extra ) {
- if ( name === "width" || name === "height" ) {
- var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
-
- function getWH() {
- val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
-
- if ( extra === "border" ) {
- return;
- }
-
- jQuery.each( which, function() {
- if ( !extra ) {
- val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
- }
-
- if ( extra === "margin" ) {
- val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
- } else {
- val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
- }
- });
- }
-
- if ( elem.offsetWidth !== 0 ) {
- getWH();
- } else {
- jQuery.swap( elem, props, getWH );
- }
-
- return Math.max(0, Math.round(val));
- }
-
- return jQuery.curCSS( elem, name, force );
- },
-
- curCSS: function( elem, name, force ) {
- var ret, style = elem.style, filter;
-
- // IE uses filters for opacity
- if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
- ret = ropacity.test(elem.currentStyle.filter || "") ?
- (parseFloat(RegExp.$1) / 100) + "" :
- "";
-
- return ret === "" ?
- "1" :
- ret;
- }
-
- // Make sure we're using the right name for getting the float value
- if ( rfloat.test( name ) ) {
- name = styleFloat;
- }
-
- if ( !force && style && style[ name ] ) {
- ret = style[ name ];
-
- } else if ( getComputedStyle ) {
-
- // Only "float" is needed here
- if ( rfloat.test( name ) ) {
- name = "float";
- }
-
- name = name.replace( rupper, "-$1" ).toLowerCase();
-
- var defaultView = elem.ownerDocument.defaultView;
-
- if ( !defaultView ) {
- return null;
- }
-
- var computedStyle = defaultView.getComputedStyle( elem, null );
-
- if ( computedStyle ) {
- ret = computedStyle.getPropertyValue( name );
- }
-
- // We should always get a number back from opacity
- if ( name === "opacity" && ret === "" ) {
- ret = "1";
- }
-
- } else if ( elem.currentStyle ) {
- var camelCase = name.replace(rdashAlpha, fcamelCase);
-
- ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
-
- // 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
- var left = style.left, rsLeft = elem.runtimeStyle.left;
-
- // Put in the new values to get a computed value out
- elem.runtimeStyle.left = elem.currentStyle.left;
- style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
- ret = style.pixelLeft + "px";
-
- // Revert the changed values
- style.left = left;
- elem.runtimeStyle.left = rsLeft;
- }
- }
-
- return ret;
- },
-
- // 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 ( var name in options ) {
- elem.style[ name ] = old[ name ];
- }
- }
-});
-
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.hidden = function( elem ) {
- var width = elem.offsetWidth, height = elem.offsetHeight,
- skip = elem.nodeName.toLowerCase() === "tr";
-
- return width === 0 && height === 0 && !skip ?
- true :
- width > 0 && height > 0 && !skip ?
- false :
- jQuery.curCSS(elem, "display") === "none";
- };
-
- jQuery.expr.filters.visible = function( elem ) {
- return !jQuery.expr.filters.hidden( elem );
- };
-}
-var jsc = now(),
- rscript = /<script(.|\s)*?\/script>/gi,
- rselectTextarea = /select|textarea/i,
- rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
- jsre = /=\?(&|$)/,
- rquery = /\?/,
- rts = /(\?|&)_=.*?(&|$)/,
- rurl = /^(\w+:)?\/\/([^\/?#]+)/,
- r20 = /%20/g,
-
- // Keep a copy of the old load method
- _load = jQuery.fn.load;
-
-jQuery.fn.extend({
- load: function( url, params, callback ) {
- if ( typeof url !== "string" ) {
- return _load.call( this, url );
-
- // 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 = null;
-
- // 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: function( res, status ) {
- // If successful, inject the HTML into all the matched elements
- if ( status === "success" || status === "notmodified" ) {
- // 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(res.responseText.replace(rscript, ""))
-
- // Locate the specified elements
- .find(selector) :
-
- // If not, just inject the full result
- res.responseText );
- }
-
- if ( callback ) {
- self.each( callback, [res.responseText, status, res] );
- }
- }
- });
-
- 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 };
- }) :
- { name: elem.name, value: val };
- }).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.extend({
-
- get: function( url, data, callback, type ) {
- // shift arguments if data argument was omited
- if ( jQuery.isFunction( data ) ) {
- type = type || callback;
- callback = data;
- data = null;
- }
-
- return jQuery.ajax({
- type: "GET",
- url: url,
- data: data,
- success: callback,
- dataType: type
- });
- },
-
- getScript: function( url, callback ) {
- return jQuery.get(url, null, callback, "script");
- },
-
- getJSON: function( url, data, callback ) {
- return jQuery.get(url, data, callback, "json");
- },
-
- post: function( url, data, callback, type ) {
- // shift arguments if data argument was omited
- if ( jQuery.isFunction( data ) ) {
- type = type || callback;
- callback = data;
- data = {};
- }
-
- return jQuery.ajax({
- type: "POST",
- url: url,
- data: data,
- success: callback,
- dataType: type
- });
- },
-
- ajaxSetup: function( settings ) {
- jQuery.extend( jQuery.ajaxSettings, settings );
- },
-
- ajaxSettings: {
- url: location.href,
- global: true,
- type: "GET",
- contentType: "application/x-www-form-urlencoded",
- processData: true,
- async: true,
- /*
- timeout: 0,
- data: null,
- username: null,
- password: null,
- traditional: false,
- */
- // Create the request object; Microsoft failed to properly
- // implement the XMLHttpRequest in IE7 (can't request local files),
- // so we use the ActiveXObject when it is available
- // This function can be overriden by calling jQuery.ajaxSetup
- xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
- function() {
- return new window.XMLHttpRequest();
- } :
- function() {
- try {
- return new window.ActiveXObject("Microsoft.XMLHTTP");
- } catch(e) {}
- },
- accepts: {
- xml: "application/xml, text/xml",
- html: "text/html",
- script: "text/javascript, application/javascript",
- json: "application/json, text/javascript",
- text: "text/plain",
- _default: "*/*"
- }
- },
-
- // Last-Modified header cache for next request
- lastModified: {},
- etag: {},
-
- ajax: function( origSettings ) {
- var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
-
- var jsonp, status, data,
- callbackContext = origSettings && origSettings.context || s,
- type = s.type.toUpperCase();
-
- // convert data if not already a string
- if ( s.data && s.processData && typeof s.data !== "string" ) {
- s.data = jQuery.param( s.data, s.traditional );
- }
-
- // Handle JSONP Parameter Callbacks
- if ( s.dataType === "jsonp" ) {
- if ( type === "GET" ) {
- if ( !jsre.test( s.url ) ) {
- s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
- }
- } else if ( !s.data || !jsre.test(s.data) ) {
- s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
- }
- s.dataType = "json";
- }
-
- // Build temporary JSONP function
- if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
- jsonp = s.jsonpCallback || ("jsonp" + jsc++);
-
- // Replace the =? sequence both in the query string and the data
- if ( s.data ) {
- s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
- }
-
- s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
- // We need to make sure
- // that a JSONP style response is executed properly
- s.dataType = "script";
-
- // Handle JSONP-style loading
- window[ jsonp ] = window[ jsonp ] || function( tmp ) {
- data = tmp;
- success();
- complete();
- // Garbage collect
- window[ jsonp ] = undefined;
-
- try {
- delete window[ jsonp ];
- } catch(e) {}
-
- if ( head ) {
- head.removeChild( script );
- }
- };
- }
-
- if ( s.dataType === "script" && s.cache === null ) {
- s.cache = false;
- }
-
- if ( s.cache === false && type === "GET" ) {
- var ts = now();
-
- // try replacing _= if it is there
- var ret = s.url.replace(rts, "$1_=" + ts + "$2");
-
- // if nothing was replaced, add timestamp to the end
- s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
- }
-
- // If data is available, append data to url for get requests
- if ( s.data && type === "GET" ) {
- s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
- }
-
- // Watch for a new set of requests
- if ( s.global && ! jQuery.active++ ) {
- jQuery.event.trigger( "ajaxStart" );
- }
-
- // Matches an absolute URL, and saves the domain
- var parts = rurl.exec( s.url ),
- remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
-
- // If we're requesting a remote document
- // and trying to load JSON or Script with a GET
- if ( s.dataType === "script" && type === "GET" && remote ) {
- var head = document.getElementsByTagName("head")[0] || document.documentElement;
- var script = document.createElement("script");
- script.src = s.url;
- if ( s.scriptCharset ) {
- script.charset = s.scriptCharset;
- }
-
- // Handle Script loading
- if ( !jsonp ) {
- var done = false;
-
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function() {
- if ( !done && (!this.readyState ||
- this.readyState === "loaded" || this.readyState === "complete") ) {
- done = true;
- success();
- complete();
-
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
- if ( head && script.parentNode ) {
- head.removeChild( script );
- }
- }
- };
- }
-
- // 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 );
-
- // We handle everything using the script element injection
- return undefined;
- }
-
- var requestDone = false;
-
- // Create the request object
- var xhr = s.xhr();
-
- if ( !xhr ) {
- return;
- }
-
- // Open the socket
- // Passing null username, generates a login popup on Opera (#2865)
- if ( s.username ) {
- xhr.open(type, s.url, s.async, s.username, s.password);
- } else {
- xhr.open(type, s.url, s.async);
- }
-
- // Need an extra try/catch for cross domain requests in Firefox 3
- try {
- // Set the correct header, if data is being sent
- if ( s.data || origSettings && origSettings.contentType ) {
- xhr.setRequestHeader("Content-Type", s.contentType);
- }
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if ( s.ifModified ) {
- if ( jQuery.lastModified[s.url] ) {
- xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
- }
-
- if ( jQuery.etag[s.url] ) {
- xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
- }
- }
-
- // Set header so the called script knows that it's an XMLHttpRequest
- // Only send the header if it's not a remote XHR
- if ( !remote ) {
- xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
- }
-
- // Set the Accepts header for the server, depending on the dataType
- xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
- s.accepts[ s.dataType ] + ", */*" :
- s.accepts._default );
- } catch(e) {}
-
- // Allow custom headers/mimetypes and early abort
- if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
- // Handle the global AJAX counter
- if ( s.global && ! --jQuery.active ) {
- jQuery.event.trigger( "ajaxStop" );
- }
-
- // close opended socket
- xhr.abort();
- return false;
- }
-
- if ( s.global ) {
- trigger("ajaxSend", [xhr, s]);
- }
-
- // Wait for a response to come back
- var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
- // The request was aborted
- if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
- // Opera doesn't call onreadystatechange before this point
- // so we simulate the call
- if ( !requestDone ) {
- complete();
- }
-
- requestDone = true;
- if ( xhr ) {
- xhr.onreadystatechange = jQuery.noop;
- }
-
- // The transfer is complete and the data is available, or the request timed out
- } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
- requestDone = true;
- xhr.onreadystatechange = jQuery.noop;
-
- status = isTimeout === "timeout" ?
- "timeout" :
- !jQuery.httpSuccess( xhr ) ?
- "error" :
- s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
- "notmodified" :
- "success";
-
- var errMsg;
-
- if ( status === "success" ) {
- // Watch for, and catch, XML document parse errors
- try {
- // process the data (runs the xml through httpData regardless of callback)
- data = jQuery.httpData( xhr, s.dataType, s );
- } catch(err) {
- status = "parsererror";
- errMsg = err;
- }
- }
-
- // Make sure that the request was successful or notmodified
- if ( status === "success" || status === "notmodified" ) {
- // JSONP handles its own success callback
- if ( !jsonp ) {
- success();
- }
- } else {
- jQuery.handleError(s, xhr, status, errMsg);
- }
-
- // Fire the complete handlers
- complete();
-
- if ( isTimeout === "timeout" ) {
- xhr.abort();
- }
-
- // Stop memory leaks
- if ( s.async ) {
- xhr = null;
- }
- }
- };
-
- // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
- // Opera doesn't fire onreadystatechange at all on abort
- try {
- var oldAbort = xhr.abort;
- xhr.abort = function() {
- if ( xhr ) {
- oldAbort.call( xhr );
- }
-
- onreadystatechange( "abort" );
- };
- } catch(e) { }
-
- // Timeout checker
- if ( s.async && s.timeout > 0 ) {
- setTimeout(function() {
- // Check to see if the request is still happening
- if ( xhr && !requestDone ) {
- onreadystatechange( "timeout" );
- }
- }, s.timeout);
- }
-
- // Send the data
- try {
- xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
- } catch(e) {
- jQuery.handleError(s, xhr, null, e);
- // Fire the complete handlers
- complete();
- }
-
- // firefox 1.5 doesn't fire statechange for sync requests
- if ( !s.async ) {
- onreadystatechange();
- }
-
- function success() {
- // If a local callback was specified, fire it and pass it the data
- if ( s.success ) {
- s.success.call( callbackContext, data, status, xhr );
- }
-
- // Fire the global callback
- if ( s.global ) {
- trigger( "ajaxSuccess", [xhr, s] );
- }
- }
-
- function complete() {
- // Process result
- if ( s.complete ) {
- s.complete.call( callbackContext, xhr, status);
- }
-
- // The request was completed
- if ( s.global ) {
- trigger( "ajaxComplete", [xhr, s] );
- }
-
- // Handle the global AJAX counter
- if ( s.global && ! --jQuery.active ) {
- jQuery.event.trigger( "ajaxStop" );
- }
- }
-
- function trigger(type, args) {
- (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
- }
-
- // return XMLHttpRequest to allow aborting the request etc.
- return xhr;
- },
-
- handleError: function( s, xhr, status, e ) {
- // If a local callback was specified, fire it
- if ( s.error ) {
- s.error.call( s.context || s, xhr, status, e );
- }
-
- // Fire the global callback
- if ( s.global ) {
- (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
- }
- },
-
- // Counter for holding the number of active queries
- active: 0,
-
- // Determines if an XMLHttpRequest was successful or not
- httpSuccess: function( xhr ) {
- try {
- // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
- return !xhr.status && location.protocol === "file:" ||
- // Opera returns 0 when status is 304
- ( xhr.status >= 200 && xhr.status < 300 ) ||
- xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
- } catch(e) {}
-
- return false;
- },
-
- // Determines if an XMLHttpRequest returns NotModified
- httpNotModified: function( xhr, url ) {
- var lastModified = xhr.getResponseHeader("Last-Modified"),
- etag = xhr.getResponseHeader("Etag");
-
- if ( lastModified ) {
- jQuery.lastModified[url] = lastModified;
- }
-
- if ( etag ) {
- jQuery.etag[url] = etag;
- }
-
- // Opera returns 0 when status is 304
- return xhr.status === 304 || xhr.status === 0;
- },
-
- httpData: function( xhr, type, s ) {
- var ct = xhr.getResponseHeader("content-type") || "",
- xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
- data = xml ? xhr.responseXML : xhr.responseText;
-
- if ( xml && data.documentElement.nodeName === "parsererror" ) {
- jQuery.error( "parsererror" );
- }
-
- // Allow a pre-filtering function to sanitize the response
- // s is checked to keep backwards compatibility
- if ( s && s.dataFilter ) {
- data = s.dataFilter( data, type );
- }
-
- // The filter can actually parse the response
- if ( typeof data === "string" ) {
- // Get the JavaScript object, if JSON is used.
- if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
- data = jQuery.parseJSON( data );
-
- // If the type is "script", eval it in global context
- } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
- jQuery.globalEval( data );
- }
- }
-
- return data;
- },
-
- // Serialize an array of form elements or a set of
- // key/values into a query string
- param: function( a, traditional ) {
- var s = [];
-
- // 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 ) {
- // 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] );
- }
- }
-
- // Return the resulting serialization
- return s.join("&").replace(r20, "+");
-
- function buildParams( prefix, obj ) {
- if ( jQuery.isArray(obj) ) {
- // Serialize array item.
- jQuery.each( obj, function( i, v ) {
- if ( traditional || /\[\]$/.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 );
- }
- });
-
- } else if ( !traditional && obj != null && typeof obj === "object" ) {
- // Serialize object item.
- jQuery.each( obj, function( k, v ) {
- buildParams( prefix + "[" + k + "]", v );
- });
-
- } else {
- // Serialize scalar item.
- add( prefix, obj );
- }
- }
-
- function add( 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);
- }
- }
-});
-var elemdisplay = {},
- rfxtypes = /toggle|show|hide/,
- rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
- timerId,
- fxAttrs = [
- // height animations
- [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
- // width animations
- [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
- // opacity animations
- [ "opacity" ]
- ];
-
-jQuery.fn.extend({
- show: function( speed, callback ) {
- if ( speed || speed === 0) {
- return this.animate( genFx("show", 3), speed, callback);
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- var old = jQuery.data(this[i], "olddisplay");
-
- this[i].style.display = old || "";
-
- if ( jQuery.css(this[i], "display") === "none" ) {
- var nodeName = this[i].nodeName, display;
-
- if ( elemdisplay[ nodeName ] ) {
- display = elemdisplay[ nodeName ];
-
- } else {
- var elem = jQuery("<" + nodeName + " />").appendTo("body");
-
- display = elem.css("display");
-
- if ( display === "none" ) {
- display = "block";
- }
-
- elem.remove();
-
- elemdisplay[ nodeName ] = display;
- }
-
- jQuery.data(this[i], "olddisplay", display);
- }
- }
-
- // Set the display of the elements in a second loop
- // to avoid the constant reflow
- for ( var j = 0, k = this.length; j < k; j++ ) {
- this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
- }
-
- return this;
- }
- },
-
- hide: function( speed, callback ) {
- if ( speed || speed === 0 ) {
- return this.animate( genFx("hide", 3), speed, callback);
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- var old = jQuery.data(this[i], "olddisplay");
- if ( !old && old !== "none" ) {
- jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
- }
- }
-
- // Set the display of the elements in a second loop
- // to avoid the constant reflow
- for ( var j = 0, k = this.length; j < k; j++ ) {
- this[j].style.display = "none";
- }
-
- return this;
- }
- },
-
- // Save the old toggle function
- _toggle: jQuery.fn.toggle,
-
- toggle: function( fn, fn2 ) {
- 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);
- }
-
- return this;
- },
-
- fadeTo: function( speed, to, callback ) {
- return this.filter(":hidden").css("opacity", 0).show().end()
- .animate({opacity: to}, speed, callback);
- },
-
- animate: function( prop, speed, easing, callback ) {
- var optall = jQuery.speed(speed, easing, callback);
-
- if ( jQuery.isEmptyObject( prop ) ) {
- return this.each( optall.complete );
- }
-
- return this[ optall.queue === false ? "each" : "queue" ](function() {
- var opt = jQuery.extend({}, optall), p,
- hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
- self = this;
-
- for ( p in prop ) {
- var name = p.replace(rdashAlpha, fcamelCase);
-
- if ( p !== name ) {
- prop[ name ] = prop[ p ];
- delete prop[ p ];
- p = name;
- }
-
- if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
- return opt.complete.call(this);
- }
-
- if ( ( p === "height" || p === "width" ) && this.style ) {
- // Store display property
- opt.display = jQuery.css(this, "display");
-
- // Make sure that nothing sneaks out
- opt.overflow = this.style.overflow;
- }
-
- if ( jQuery.isArray( prop[p] ) ) {
- // Create (if needed) and add to specialEasing
- (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
- prop[p] = prop[p][0];
- }
- }
-
- if ( opt.overflow != null ) {
- this.style.overflow = "hidden";
- }
-
- opt.curAnim = jQuery.extend({}, prop);
-
- jQuery.each( prop, function( name, val ) {
- var e = new jQuery.fx( self, opt, name );
-
- if ( rfxtypes.test(val) ) {
- e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-
- } else {
- var parts = rfxnum.exec(val),
- start = e.cur(true) || 0;
-
- if ( parts ) {
- var end = parseFloat( parts[2] ),
- unit = parts[3] || "px";
-
- // We need to compute starting value
- if ( unit !== "px" ) {
- self.style[ name ] = (end || 1) + unit;
- start = ((end || 1) / e.cur(true)) * start;
- self.style[ name ] = 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 ) {
- var timers = jQuery.timers;
-
- if ( clearQueue ) {
- this.queue([]);
- }
-
- this.each(function() {
- // go in reverse order so anything added to the queue during the loop is ignored
- for ( var i = timers.length - 1; i >= 0; 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;
- }
-
-});
-
-// 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" }
-}, function( name, props ) {
- jQuery.fn[ name ] = function( speed, callback ) {
- return this.animate( props, speed, callback );
- };
-});
-
-jQuery.extend({
- speed: function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? 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 :
- jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
-
- // Queueing
- opt.old = opt.complete;
- opt.complete = function() {
- if ( opt.queue !== false ) {
- jQuery(this).dequeue();
- }
- if ( jQuery.isFunction( opt.old ) ) {
- opt.old.call( 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;
-
- if ( !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 );
-
- // Set display property to block for height/width animations
- if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
- this.elem.style.display = "block";
- }
- },
-
- // Get the current size
- cur: function( force ) {
- if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
- return this.elem[ this.prop ];
- }
-
- var r = parseFloat(jQuery.css(this.elem, this.prop, force));
- return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
- },
-
- // Start an animation from one number to another
- custom: function( from, to, unit ) {
- this.startTime = now();
- this.start = from;
- this.end = to;
- this.unit = unit || this.unit || "px";
- this.now = this.start;
- this.pos = this.state = 0;
-
- var self = this;
- function t( gotoEnd ) {
- return self.step(gotoEnd);
- }
-
- t.elem = this.elem;
-
- if ( t() && jQuery.timers.push(t) && !timerId ) {
- timerId = setInterval(jQuery.fx.tick, 13);
- }
- },
-
- // 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 = now(), done = true;
-
- if ( gotoEnd || t >= this.options.duration + this.startTime ) {
- this.now = this.end;
- this.pos = this.state = 1;
- this.update();
-
- this.options.curAnim[ this.prop ] = true;
-
- for ( var i in this.options.curAnim ) {
- if ( this.options.curAnim[i] !== true ) {
- done = false;
- }
- }
-
- if ( done ) {
- if ( this.options.display != null ) {
- // Reset the overflow
- this.elem.style.overflow = this.options.overflow;
-
- // Reset the display
- var old = jQuery.data(this.elem, "olddisplay");
- this.elem.style.display = old ? old : this.options.display;
-
- if ( jQuery.css(this.elem, "display") === "none" ) {
- this.elem.style.display = "block";
- }
- }
-
- // Hide the element if the "hide" operation was done
- if ( this.options.hide ) {
- jQuery(this.elem).hide();
- }
-
- // Reset the properties, if the item has been hidden or shown
- if ( this.options.hide || this.options.show ) {
- for ( var p in this.options.curAnim ) {
- jQuery.style(this.elem, p, this.options.orig[p]);
- }
- }
-
- // Execute the complete function
- this.options.complete.call( this.elem );
- }
-
- return false;
-
- } else {
- var n = t - this.startTime;
- this.state = n / this.options.duration;
-
- // Perform the easing function, defaults to swing
- var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
- var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
- this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.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() {
- var timers = jQuery.timers;
-
- for ( var i = 0; i < timers.length; i++ ) {
- if ( !timers[i]() ) {
- timers.splice(i--, 1);
- }
- }
-
- if ( !timers.length ) {
- jQuery.fx.stop();
- }
- },
-
- 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;
- };
-}
-
-function genFx( type, num ) {
- var obj = {};
-
- jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
- obj[ this ] = type;
- });
-
- return obj;
-}
-if ( "getBoundingClientRect" in document.documentElement ) {
- 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 );
- }
-
- var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
- clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
- top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
- left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.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 offsetParent = elem.offsetParent, prevOffsetParent = elem,
- doc = elem.ownerDocument, computedStyle, 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 && /^t(able|d|h)$/i.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.curCSS(body, "marginTop", true) ) || 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 );
- body = container = innerDiv = checkDiv = table = td = null;
- 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.curCSS(body, "marginTop", true) ) || 0;
- left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
- }
-
- return { top: top, left: left };
- },
-
- setOffset: function( elem, options, i ) {
- // set position first, in-case top/left are set even on static elem
- if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
- elem.style.position = "relative";
- }
- var curElem = jQuery( elem ),
- curOffset = curElem.offset(),
- curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
- curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
-
- if ( jQuery.isFunction( options ) ) {
- options = options.call( elem, i, curOffset );
- }
-
- var props = {
- top: (options.top - curOffset.top) + curTop,
- 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 = /^body|html$/i.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.curCSS(elem, "marginTop", true) ) || 0;
- offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
-
- // Add offsetParent borders
- parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
- parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 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 && (!/^body|html$/i.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 = this[0], win;
-
- if ( !elem ) {
- return null;
- }
-
- if ( val !== undefined ) {
- // 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;
- }
- });
- } else {
- 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 ];
- }
- };
-});
-
-function getWindow( elem ) {
- return ("scrollTo" in elem && elem.document) ?
- elem :
- elem.nodeType === 9 ?
- elem.defaultView || elem.parentWindow :
- false;
-}
-// Create 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() {
- return this[0] ?
- jQuery.css( this[0], type, false, "padding" ) :
- null;
- };
-
- // outerHeight and outerWidth
- jQuery.fn["outer" + name] = function( margin ) {
- return this[0] ?
- jQuery.css( this[0], type, false, 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 ]() ) );
- });
- }
-
- return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
- // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
- elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
- elem.document.body[ "client" + name ] :
-
- // Get document width or height
- (elem.nodeType === 9) ? // is it a document
- // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
- 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
- size === undefined ?
- // Get width or height on the element
- jQuery.css( elem, type ) :
-
- // Set the width or height on the element (default to pixels if value is unitless)
- this.css( type, typeof size === "string" ? size : size + "px" );
- };
-
-});
-// Expose jQuery to the global object
-window.jQuery = window.$ = jQuery;
-
-})(window);
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.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(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.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|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={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,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<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>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
Binary file web/data/tab.png has changed
--- a/web/data/ui.core.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,431 +0,0 @@
-/*
- * jQuery UI @VERSION
- *
- * Copyright (c) 2010 Paul Bakaus (ui.jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI
- */
-;(function($) {
-
-/** jQuery core modifications and additions **/
-
-var _remove = $.fn.remove;
-$.fn.remove = function() {
- $("*", this).add(this).triggerHandler("remove");
- return _remove.apply(this, arguments );
-};
-
-function isVisible(element) {
- function checkStyles(element) {
- var style = element.style;
- return (style.display != 'none' && style.visibility != 'hidden');
- }
-
- var visible = checkStyles(element);
-
- (visible && $.each($.dir(element, 'parentNode'), function() {
- return (visible = checkStyles(this));
- }));
-
- return visible;
-}
-
-$.extend($.expr[':'], {
- data: function(a, i, m) {
- return $.data(a, m[3]);
- },
-
- // TODO: add support for object, area
- tabbable: function(a, i, m) {
- var nodeName = a.nodeName.toLowerCase();
-
- return (
- // in tab order
- a.tabIndex >= 0 &&
-
- ( // filter node types that participate in the tab order
-
- // anchor tag
- ('a' == nodeName && a.href) ||
-
- // enabled form element
- (/input|select|textarea|button/.test(nodeName) &&
- 'hidden' != a.type && !a.disabled)
- ) &&
-
- // visible on page
- isVisible(a)
- );
- }
-});
-
-$.keyCode = {
- BACKSPACE: 8,
- CAPS_LOCK: 20,
- COMMA: 188,
- CONTROL: 17,
- DELETE: 46,
- DOWN: 40,
- END: 35,
- ENTER: 13,
- ESCAPE: 27,
- HOME: 36,
- INSERT: 45,
- LEFT: 37,
- NUMPAD_ADD: 107,
- NUMPAD_DECIMAL: 110,
- NUMPAD_DIVIDE: 111,
- NUMPAD_ENTER: 108,
- NUMPAD_MULTIPLY: 106,
- NUMPAD_SUBTRACT: 109,
- PAGE_DOWN: 34,
- PAGE_UP: 33,
- PERIOD: 190,
- RIGHT: 39,
- SHIFT: 16,
- SPACE: 32,
- TAB: 9,
- UP: 38
-};
-
-// $.widget is a factory to create jQuery plugins
-// taking some boilerplate code out of the plugin code
-// created by Scott González and Jörn Zaefferer
-function getter(namespace, plugin, method, args) {
- function getMethods(type) {
- var methods = $[namespace][plugin][type] || [];
- return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
- }
-
- var methods = getMethods('getter');
- if (args.length == 1 && typeof args[0] == 'string') {
- methods = methods.concat(getMethods('getterSetter'));
- }
- return ($.inArray(method, methods) != -1);
-}
-
-$.widget = function(name, prototype) {
- var namespace = name.split(".")[0];
- name = name.split(".")[1];
-
- // create plugin method
- $.fn[name] = function(options) {
- var isMethodCall = (typeof options == 'string'),
- args = Array.prototype.slice.call(arguments, 1);
-
- // prevent calls to internal methods
- if (isMethodCall && options.substring(0, 1) == '_') {
- return this;
- }
-
- // handle getter methods
- if (isMethodCall && getter(namespace, name, options, args)) {
- var instance = $.data(this[0], name);
- return (instance ? instance[options].apply(instance, args)
- : undefined);
- }
-
- // handle initialization and non-getter methods
- return this.each(function() {
- var instance = $.data(this, name);
-
- // constructor
- (!instance && !isMethodCall &&
- $.data(this, name, new $[namespace][name](this, options)));
-
- // method call
- (instance && isMethodCall && $.isFunction(instance[options]) &&
- instance[options].apply(instance, args));
- });
- };
-
- // create widget constructor
- $[namespace][name] = function(element, options) {
- var self = this;
-
- this.widgetName = name;
- this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
- this.widgetBaseClass = namespace + '-' + name;
-
- this.options = $.extend({},
- $.widget.defaults,
- $[namespace][name].defaults,
- $.metadata && $.metadata.get(element)[name],
- options);
-
- this.element = $(element)
- .bind('setData.' + name, function(e, key, value) {
- return self._setData(key, value);
- })
- .bind('getData.' + name, function(e, key) {
- return self._getData(key);
- })
- .bind('remove', function() {
- return self.destroy();
- });
-
- this._init();
- };
-
- // add widget prototype
- $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
-
- // TODO: merge getter and getterSetter properties from widget prototype
- // and plugin prototype
- $[namespace][name].getterSetter = 'option';
-};
-
-$.widget.prototype = {
- _init: function() {},
- destroy: function() {
- this.element.removeData(this.widgetName);
- },
-
- option: function(key, value) {
- var options = key,
- self = this;
-
- if (typeof key == "string") {
- if (value === undefined) {
- return this._getData(key);
- }
- options = {};
- options[key] = value;
- }
-
- $.each(options, function(key, value) {
- self._setData(key, value);
- });
- },
- _getData: function(key) {
- return this.options[key];
- },
- _setData: function(key, value) {
- this.options[key] = value;
-
- if (key == 'disabled') {
- this.element[value ? 'addClass' : 'removeClass'](
- this.widgetBaseClass + '-disabled');
- }
- },
-
- enable: function() {
- this._setData('disabled', false);
- },
- disable: function() {
- this._setData('disabled', true);
- },
-
- _trigger: function(type, e, data) {
- var eventName = (type == this.widgetEventPrefix
- ? type : this.widgetEventPrefix + type);
- e = e || $.event.fix({ type: eventName, target: this.element[0] });
- return this.element.triggerHandler(eventName, [e, data], this.options[type]);
- }
-};
-
-$.widget.defaults = {
- disabled: false
-};
-
-
-/** jQuery UI core **/
-
-$.ui = {
- plugin: {
- add: function(module, option, set) {
- var proto = $.ui[module].prototype;
- for(var i in set) {
- proto.plugins[i] = proto.plugins[i] || [];
- proto.plugins[i].push([option, set[i]]);
- }
- },
- call: function(instance, name, args) {
- var set = instance.plugins[name];
- if(!set) { return; }
-
- for (var i = 0; i < set.length; i++) {
- if (instance.options[set[i][0]]) {
- set[i][1].apply(instance.element, args);
- }
- }
- }
- },
- cssCache: {},
- css: function(name) {
- if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
- var tmp = $('<div class="ui-gen">').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');
-
- //if (!$.browser.safari)
- //tmp.appendTo('body');
-
- //Opera and Safari set width and height to 0px instead of auto
- //Safari returns rgba(0,0,0,0) when bgcolor is not set
- $.ui.cssCache[name] = !!(
- (!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) ||
- !(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
- );
- try { $('body').get(0).removeChild(tmp.get(0)); } catch(e){}
- return $.ui.cssCache[name];
- },
- disableSelection: function(el) {
- $(el)
- .attr('unselectable', 'on')
- .css('MozUserSelect', 'none')
- .bind('selectstart.ui', function() { return false; });
- },
- enableSelection: function(el) {
- $(el)
- .attr('unselectable', 'off')
- .css('MozUserSelect', '')
- .unbind('selectstart.ui');
- },
- hasScroll: function(e, a) {
- var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
- has = false;
-
- if (e[scroll] > 0) { return true; }
-
- // TODO: determine which cases actually cause this to happen
- // if the element doesn't have the scroll set, see if it's possible to
- // set the scroll
- e[scroll] = 1;
- has = (e[scroll] > 0);
- e[scroll] = 0;
- return has;
- }
-};
-
-
-/** Mouse Interaction Plugin **/
-
-$.ui.mouse = {
- _mouseInit: function() {
- var self = this;
-
- this.element.bind('mousedown.'+this.widgetName, function(e) {
- return self._mouseDown(e);
- });
-
- // Prevent text selection in IE
- if ($.browser.msie) {
- this._mouseUnselectable = this.element.attr('unselectable');
- this.element.attr('unselectable', 'on');
- }
-
- this.started = false;
- },
-
- // TODO: make sure destroying one instance of mouse doesn't mess with
- // other instances of mouse
- _mouseDestroy: function() {
- this.element.unbind('.'+this.widgetName);
-
- // Restore text selection in IE
- ($.browser.msie
- && this.element.attr('unselectable', this._mouseUnselectable));
- },
-
- _mouseDown: function(e) {
- // we may have missed mouseup (out of window)
- (this._mouseStarted && this._mouseUp(e));
-
- this._mouseDownEvent = e;
-
- var self = this,
- btnIsLeft = (e.which == 1),
- elIsCancel = (typeof this.options.cancel == "string" ? $(e.target).parents().add(e.target).filter(this.options.cancel).length : false);
- if (!btnIsLeft || elIsCancel || !this._mouseCapture(e)) {
- return true;
- }
-
- this.mouseDelayMet = !this.options.delay;
- if (!this.mouseDelayMet) {
- this._mouseDelayTimer = setTimeout(function() {
- self.mouseDelayMet = true;
- }, this.options.delay);
- }
-
- if (this._mouseDistanceMet(e) && this._mouseDelayMet(e)) {
- this._mouseStarted = (this._mouseStart(e) !== false);
- if (!this._mouseStarted) {
- e.preventDefault();
- return true;
- }
- }
-
- // these delegates are required to keep context
- this._mouseMoveDelegate = function(e) {
- return self._mouseMove(e);
- };
- this._mouseUpDelegate = function(e) {
- return self._mouseUp(e);
- };
- $(document)
- .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
- .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
-
- return false;
- },
-
- _mouseMove: function(e) {
- // IE mouseup check - mouseup happened when mouse was out of window
- if ($.browser.msie && !e.button) {
- return this._mouseUp(e);
- }
-
- if (this._mouseStarted) {
- this._mouseDrag(e);
- return false;
- }
-
- if (this._mouseDistanceMet(e) && this._mouseDelayMet(e)) {
- this._mouseStarted =
- (this._mouseStart(this._mouseDownEvent, e) !== false);
- (this._mouseStarted ? this._mouseDrag(e) : this._mouseUp(e));
- }
-
- return !this._mouseStarted;
- },
-
- _mouseUp: function(e) {
- $(document)
- .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
- .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
-
- if (this._mouseStarted) {
- this._mouseStarted = false;
- this._mouseStop(e);
- }
-
- return false;
- },
-
- _mouseDistanceMet: function(e) {
- return (Math.max(
- Math.abs(this._mouseDownEvent.pageX - e.pageX),
- Math.abs(this._mouseDownEvent.pageY - e.pageY)
- ) >= this.options.distance
- );
- },
-
- _mouseDelayMet: function(e) {
- return this.mouseDelayMet;
- },
-
- // These are placeholder methods, to be overriden by extending plugin
- _mouseStart: function(e) {},
- _mouseDrag: function(e) {},
- _mouseStop: function(e) {},
- _mouseCapture: function(e) { return true; }
-};
-
-$.ui.mouse.defaults = {
- cancel: null,
- distance: 1,
- delay: 0
-};
-
-})(jQuery);
--- a/web/data/ui.slider.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * jQuery UI 1.7.1
- *
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI
- */
jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.1",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/*
- * jQuery UI Draggable 1.7.1
- *
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI/Draggables
- *
- * Depends:
- * ui.core.js
- */
(function(a){a.widget("ui.draggable",a.extend({},a.ui.mouse,{_init:function(){if(this.options.helper=="original"&&!(/^(?:r|a|f)/).test(this.element.css("position"))){this.element[0].style.position="relative"}(this.options.addClasses&&this.element.addClass("ui-draggable"));(this.options.disabled&&this.element.addClass("ui-draggable-disabled"));this._mouseInit()},destroy:function(){if(!this.element.data("draggable")){return}this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy()},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")){return false}this.handle=this._getHandle(b);if(!this.handle){return false}return true},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b);this._cacheHelperProportions();if(a.ui.ddmanager){a.ui.ddmanager.current=this}this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(b);this.originalPageX=b.pageX;this.originalPageY=b.pageY;if(c.cursorAt){this._adjustOffsetFromHelper(c.cursorAt)}if(c.containment){this._setContainment()}this._trigger("start",b);this._cacheHelperProportions();if(a.ui.ddmanager&&!c.dropBehaviour){a.ui.ddmanager.prepareOffsets(this,b)}this.helper.addClass("ui-draggable-dragging");this._mouseDrag(b,true);return true},_mouseDrag:function(b,d){this.position=this._generatePosition(b);this.positionAbs=this._convertPositionTo("absolute");if(!d){var c=this._uiHash();this._trigger("drag",b,c);this.position=c.position}if(!this.options.axis||this.options.axis!="y"){this.helper[0].style.left=this.position.left+"px"}if(!this.options.axis||this.options.axis!="x"){this.helper[0].style.top=this.position.top+"px"}if(a.ui.ddmanager){a.ui.ddmanager.drag(this,b)}return false},_mouseStop:function(c){var d=false;if(a.ui.ddmanager&&!this.options.dropBehaviour){d=a.ui.ddmanager.drop(this,c)}if(this.dropped){d=this.dropped;this.dropped=false}if((this.options.revert=="invalid"&&!d)||(this.options.revert=="valid"&&d)||this.options.revert===true||(a.isFunction(this.options.revert)&&this.options.revert.call(this.element,d))){var b=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){b._trigger("stop",c);b._clear()})}else{this._trigger("stop",c);this._clear()}return false},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?true:false;a(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==b.target){c=true}});return c},_createHelper:function(c){var d=this.options;var b=a.isFunction(d.helper)?a(d.helper.apply(this.element[0],[c])):(d.helper=="clone"?this.element.clone():this.element);if(!b.parents("body").length){b.appendTo((d.appendTo=="parent"?this.element[0].parentNode:d.appendTo))}if(b[0]!=this.element[0]&&!(/(fixed|absolute)/).test(b.css("position"))){b.css("position","absolute")}return b},_adjustOffsetFromHelper:function(b){if(b.left!=undefined){this.offset.click.left=b.left+this.margins.left}if(b.right!=undefined){this.offset.click.left=this.helperProportions.width-b.right+this.margins.left}if(b.top!=undefined){this.offset.click.top=b.top+this.margins.top}if(b.bottom!=undefined){this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top}},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])){b.left+=this.scrollParent.scrollLeft();b.top+=this.scrollParent.scrollTop()}if((this.offsetParent[0]==document.body)||(this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)){b={top:0,left:0}}return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var b=this.element.position();return{top:b.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:b.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else{return{top:0,left:0}}},_cacheMargins:function(){this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e=this.options;if(e.containment=="parent"){e.containment=this.helper[0].parentNode}if(e.containment=="document"||e.containment=="window"){this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(e.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(e.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]}if(!(/^(document|window|parent)$/).test(e.containment)&&e.containment.constructor!=Array){var c=a(e.containment)[0];if(!c){return}var d=a(e.containment).offset();var b=(a(c).css("overflow")!="hidden");this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(b?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(b?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}else{if(e.containment.constructor==Array){this.containment=e.containment}}},_convertPositionTo:function(f,h){if(!h){h=this.position}var c=f=="absolute"?1:-1;var e=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=(/(html|body)/i).test(b[0].tagName);return{top:(h.top+this.offset.relative.top*c+this.offset.parent.top*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(g?0:b.scrollTop()))*c)),left:(h.left+this.offset.relative.left*c+this.offset.parent.left*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:b.scrollLeft())*c))}},_generatePosition:function(e){var h=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,i=(/(html|body)/i).test(b[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0])){this.offset.relative=this._getRelativeOffset()}var d=e.pageX;var c=e.pageY;if(this.originalPosition){if(this.containment){if(e.pageX-this.offset.click.left<this.containment[0]){d=this.containment[0]+this.offset.click.left}if(e.pageY-this.offset.click.top<this.containment[1]){c=this.containment[1]+this.offset.click.top}if(e.pageX-this.offset.click.left>this.containment[2]){d=this.containment[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>this.containment[3]){c=this.containment[3]+this.offset.click.top}}if(h.grid){var g=this.originalPageY+Math.round((c-this.originalPageY)/h.grid[1])*h.grid[1];c=this.containment?(!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:(!(g-this.offset.click.top<this.containment[1])?g-h.grid[1]:g+h.grid[1])):g;var f=this.originalPageX+Math.round((d-this.originalPageX)/h.grid[0])*h.grid[0];d=this.containment?(!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:(!(f-this.offset.click.left<this.containment[0])?f-h.grid[0]:f+h.grid[0])):f}}return{top:(c-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(i?0:b.scrollTop())))),left:(d-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():i?0:b.scrollLeft())))}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");if(this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval){this.helper.remove()}this.helper=null;this.cancelHelperRemoval=false},_trigger:function(b,c,d){d=d||this._uiHash();a.ui.plugin.call(this,b,[c,d]);if(b=="drag"){this.positionAbs=this._convertPositionTo("absolute")}return a.widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(b){return{helper:this.helper,position:this.position,absolutePosition:this.positionAbs,offset:this.positionAbs}}}));a.extend(a.ui.draggable,{version:"1.7.1",eventPrefix:"drag",defaults:{addClasses:true,appendTo:"parent",axis:false,cancel:":input,option",connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,delay:0,distance:1,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false}});a.ui.plugin.add("draggable","connectToSortable",{start:function(c,e){var d=a(this).data("draggable"),f=d.options,b=a.extend({},e,{item:d.element});d.sortables=[];a(f.connectToSortable).each(function(){var g=a.data(this,"sortable");if(g&&!g.options.disabled){d.sortables.push({instance:g,shouldRevert:g.options.revert});g._refreshItems();g._trigger("activate",c,b)}})},stop:function(c,e){var d=a(this).data("draggable"),b=a.extend({},e,{item:d.element});a.each(d.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;d.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert){this.instance.options.revert=true}this.instance._mouseStop(c);this.instance.options.helper=this.instance.options._helper;if(d.options.helper=="original"){this.instance.currentItem.css({top:"auto",left:"auto"})}}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",c,b)}})},drag:function(c,f){var e=a(this).data("draggable"),b=this;var d=function(i){var n=this.offset.click.top,m=this.offset.click.left;var g=this.positionAbs.top,k=this.positionAbs.left;var j=i.height,l=i.width;var p=i.top,h=i.left;return a.ui.isOver(g+n,k+m,p,h,j,l)};a.each(e.sortables,function(g){this.instance.positionAbs=e.positionAbs;this.instance.helperProportions=e.helperProportions;this.instance.offset.click=e.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=a(b).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return f.helper[0]};c.target=this.instance.currentItem[0];this.instance._mouseCapture(c,true);this.instance._mouseStart(c,true,true);this.instance.offset.click.top=e.offset.click.top;this.instance.offset.click.left=e.offset.click.left;this.instance.offset.parent.left-=e.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=e.offset.parent.top-this.instance.offset.parent.top;e._trigger("toSortable",c);e.dropped=this.instance.element;e.currentItem=e.element;this.instance.fromOutside=e}if(this.instance.currentItem){this.instance._mouseDrag(c)}}else{if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",c,this.instance._uiHash(this.instance));this.instance._mouseStop(c,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();if(this.instance.placeholder){this.instance.placeholder.remove()}e._trigger("fromSortable",c);e.dropped=false}}})}});a.ui.plugin.add("draggable","cursor",{start:function(c,d){var b=a("body"),e=a(this).data("draggable").options;if(b.css("cursor")){e._cursor=b.css("cursor")}b.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._cursor){a("body").css("cursor",d._cursor)}}});a.ui.plugin.add("draggable","iframeFix",{start:function(b,c){var d=a(this).data("draggable").options;a(d.iframeFix===true?"iframe":d.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css(a(this).offset()).appendTo("body")})},stop:function(b,c){a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});a.ui.plugin.add("draggable","opacity",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("opacity")){e._opacity=b.css("opacity")}b.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._opacity){a(c.helper).css("opacity",d._opacity)}}});a.ui.plugin.add("draggable","scroll",{start:function(c,d){var b=a(this).data("draggable");if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){b.overflowOffset=b.scrollParent.offset()}},drag:function(d,e){var c=a(this).data("draggable"),f=c.options,b=false;if(c.scrollParent[0]!=document&&c.scrollParent[0].tagName!="HTML"){if(!f.axis||f.axis!="x"){if((c.overflowOffset.top+c.scrollParent[0].offsetHeight)-d.pageY<f.scrollSensitivity){c.scrollParent[0].scrollTop=b=c.scrollParent[0].scrollTop+f.scrollSpeed}else{if(d.pageY-c.overflowOffset.top<f.scrollSensitivity){c.scrollParent[0].scrollTop=b=c.scrollParent[0].scrollTop-f.scrollSpeed}}}if(!f.axis||f.axis!="y"){if((c.overflowOffset.left+c.scrollParent[0].offsetWidth)-d.pageX<f.scrollSensitivity){c.scrollParent[0].scrollLeft=b=c.scrollParent[0].scrollLeft+f.scrollSpeed}else{if(d.pageX-c.overflowOffset.left<f.scrollSensitivity){c.scrollParent[0].scrollLeft=b=c.scrollParent[0].scrollLeft-f.scrollSpeed}}}}else{if(!f.axis||f.axis!="x"){if(d.pageY-a(document).scrollTop()<f.scrollSensitivity){b=a(document).scrollTop(a(document).scrollTop()-f.scrollSpeed)}else{if(a(window).height()-(d.pageY-a(document).scrollTop())<f.scrollSensitivity){b=a(document).scrollTop(a(document).scrollTop()+f.scrollSpeed)}}}if(!f.axis||f.axis!="y"){if(d.pageX-a(document).scrollLeft()<f.scrollSensitivity){b=a(document).scrollLeft(a(document).scrollLeft()-f.scrollSpeed)}else{if(a(window).width()-(d.pageX-a(document).scrollLeft())<f.scrollSensitivity){b=a(document).scrollLeft(a(document).scrollLeft()+f.scrollSpeed)}}}}if(b!==false&&a.ui.ddmanager&&!f.dropBehaviour){a.ui.ddmanager.prepareOffsets(c,d)}}});a.ui.plugin.add("draggable","snap",{start:function(c,d){var b=a(this).data("draggable"),e=b.options;b.snapElements=[];a(e.snap.constructor!=String?(e.snap.items||":data(draggable)"):e.snap).each(function(){var g=a(this);var f=g.offset();if(this!=b.element[0]){b.snapElements.push({item:this,width:g.outerWidth(),height:g.outerHeight(),top:f.top,left:f.left})}})},drag:function(u,p){var g=a(this).data("draggable"),q=g.options;var y=q.snapTolerance;var x=p.offset.left,w=x+g.helperProportions.width,f=p.offset.top,e=f+g.helperProportions.height;for(var v=g.snapElements.length-1;v>=0;v--){var s=g.snapElements[v].left,n=s+g.snapElements[v].width,m=g.snapElements[v].top,A=m+g.snapElements[v].height;if(!((s-y<x&&x<n+y&&m-y<f&&f<A+y)||(s-y<x&&x<n+y&&m-y<e&&e<A+y)||(s-y<w&&w<n+y&&m-y<f&&f<A+y)||(s-y<w&&w<n+y&&m-y<e&&e<A+y))){if(g.snapElements[v].snapping){(g.options.snap.release&&g.options.snap.release.call(g.element,u,a.extend(g._uiHash(),{snapItem:g.snapElements[v].item})))}g.snapElements[v].snapping=false;continue}if(q.snapMode!="inner"){var c=Math.abs(m-e)<=y;var z=Math.abs(A-f)<=y;var j=Math.abs(s-w)<=y;var k=Math.abs(n-x)<=y;if(c){p.position.top=g._convertPositionTo("relative",{top:m-g.helperProportions.height,left:0}).top-g.margins.top}if(z){p.position.top=g._convertPositionTo("relative",{top:A,left:0}).top-g.margins.top}if(j){p.position.left=g._convertPositionTo("relative",{top:0,left:s-g.helperProportions.width}).left-g.margins.left}if(k){p.position.left=g._convertPositionTo("relative",{top:0,left:n}).left-g.margins.left}}var h=(c||z||j||k);if(q.snapMode!="outer"){var c=Math.abs(m-f)<=y;var z=Math.abs(A-e)<=y;var j=Math.abs(s-x)<=y;var k=Math.abs(n-w)<=y;if(c){p.position.top=g._convertPositionTo("relative",{top:m,left:0}).top-g.margins.top}if(z){p.position.top=g._convertPositionTo("relative",{top:A-g.helperProportions.height,left:0}).top-g.margins.top}if(j){p.position.left=g._convertPositionTo("relative",{top:0,left:s}).left-g.margins.left}if(k){p.position.left=g._convertPositionTo("relative",{top:0,left:n-g.helperProportions.width}).left-g.margins.left}}if(!g.snapElements[v].snapping&&(c||z||j||k||h)){(g.options.snap.snap&&g.options.snap.snap.call(g.element,u,a.extend(g._uiHash(),{snapItem:g.snapElements[v].item})))}g.snapElements[v].snapping=(c||z||j||k||h)}}});a.ui.plugin.add("draggable","stack",{start:function(b,c){var e=a(this).data("draggable").options;var d=a.makeArray(a(e.stack.group)).sort(function(g,f){return(parseInt(a(g).css("zIndex"),10)||e.stack.min)-(parseInt(a(f).css("zIndex"),10)||e.stack.min)});a(d).each(function(f){this.style.zIndex=e.stack.min+f});this[0].style.zIndex=e.stack.min+d.length}});a.ui.plugin.add("draggable","zIndex",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("zIndex")){e._zIndex=b.css("zIndex")}b.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._zIndex){a(c.helper).css("zIndex",d._zIndex)}}})})(jQuery);;/*
- * jQuery UI Droppable 1.7.1
- *
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI/Droppables
- *
- * Depends:
- * ui.core.js
- * ui.draggable.js
- */
(function(a){a.widget("ui.droppable",{_init:function(){var c=this.options,b=c.accept;this.isover=0;this.isout=1;this.options.accept=this.options.accept&&a.isFunction(this.options.accept)?this.options.accept:function(e){return e.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};a.ui.ddmanager.droppables[this.options.scope]=a.ui.ddmanager.droppables[this.options.scope]||[];a.ui.ddmanager.droppables[this.options.scope].push(this);(this.options.addClasses&&this.element.addClass("ui-droppable"))},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++){if(b[c]==this){b.splice(c,1)}}this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable")},_setData:function(b,c){if(b=="accept"){this.options.accept=c&&a.isFunction(c)?c:function(e){return e.is(c)}}else{a.widget.prototype._setData.apply(this,arguments)}},_activate:function(c){var b=a.ui.ddmanager.current;if(this.options.activeClass){this.element.addClass(this.options.activeClass)}(b&&this._trigger("activate",c,this.ui(b)))},_deactivate:function(c){var b=a.ui.ddmanager.current;if(this.options.activeClass){this.element.removeClass(this.options.activeClass)}(b&&this._trigger("deactivate",c,this.ui(b)))},_over:function(c){var b=a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.hoverClass){this.element.addClass(this.options.hoverClass)}this._trigger("over",c,this.ui(b))}},_out:function(c){var b=a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.hoverClass){this.element.removeClass(this.options.hoverClass)}this._trigger("out",c,this.ui(b))}},_drop:function(c,d){var b=d||a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return false}var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var f=a.data(this,"droppable");if(f.options.greedy&&a.ui.intersect(b,a.extend(f,{offset:f.element.offset()}),f.options.tolerance)){e=true;return false}});if(e){return false}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.activeClass){this.element.removeClass(this.options.activeClass)}if(this.options.hoverClass){this.element.removeClass(this.options.hoverClass)}this._trigger("drop",c,this.ui(b));return this.element}return false},ui:function(b){return{draggable:(b.currentItem||b.element),helper:b.helper,position:b.position,absolutePosition:b.positionAbs,offset:b.positionAbs}}});a.extend(a.ui.droppable,{version:"1.7.1",eventPrefix:"drop",defaults:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"}});a.ui.intersect=function(q,j,o){if(!j.offset){return false}var e=(q.positionAbs||q.position.absolute).left,d=e+q.helperProportions.width,n=(q.positionAbs||q.position.absolute).top,m=n+q.helperProportions.height;var g=j.offset.left,c=g+j.proportions.width,p=j.offset.top,k=p+j.proportions.height;switch(o){case"fit":return(g<e&&d<c&&p<n&&m<k);break;case"intersect":return(g<e+(q.helperProportions.width/2)&&d-(q.helperProportions.width/2)<c&&p<n+(q.helperProportions.height/2)&&m-(q.helperProportions.height/2)<k);break;case"pointer":var h=((q.positionAbs||q.position.absolute).left+(q.clickOffset||q.offset.click).left),i=((q.positionAbs||q.position.absolute).top+(q.clickOffset||q.offset.click).top),f=a.ui.isOver(i,h,p,g,j.proportions.height,j.proportions.width);return f;break;case"touch":return((n>=p&&n<=k)||(m>=p&&m<=k)||(n<p&&m>k))&&((e>=g&&e<=c)||(d>=g&&d<=c)||(e<g&&d>c));break;default:return false;break}};a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,g){var b=a.ui.ddmanager.droppables[e.options.scope];var f=g?g.type:null;var h=(e.currentItem||e.element).find(":data(droppable)").andSelf();droppablesLoop:for(var d=0;d<b.length;d++){if(b[d].options.disabled||(e&&!b[d].options.accept.call(b[d].element[0],(e.currentItem||e.element)))){continue}for(var c=0;c<h.length;c++){if(h[c]==b[d].element[0]){b[d].proportions.height=0;continue droppablesLoop}}b[d].visible=b[d].element.css("display")!="none";if(!b[d].visible){continue}b[d].offset=b[d].element.offset();b[d].proportions={width:b[d].element[0].offsetWidth,height:b[d].element[0].offsetHeight};if(f=="mousedown"){b[d]._activate.call(b[d],g)}}},drop:function(b,c){var d=false;a.each(a.ui.ddmanager.droppables[b.options.scope],function(){if(!this.options){return}if(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)){d=this._drop.call(this,c)}if(!this.options.disabled&&this.visible&&this.options.accept.call(this.element[0],(b.currentItem||b.element))){this.isout=1;this.isover=0;this._deactivate.call(this,c)}});return d},drag:function(b,c){if(b.options.refreshPositions){a.ui.ddmanager.prepareOffsets(b,c)}a.each(a.ui.ddmanager.droppables[b.options.scope],function(){if(this.options.disabled||this.greedyChild||!this.visible){return}var e=a.ui.intersect(b,this,this.options.tolerance);var g=!e&&this.isover==1?"isout":(e&&this.isover==0?"isover":null);if(!g){return}var f;if(this.options.greedy){var d=this.element.parents(":data(droppable):eq(0)");if(d.length){f=a.data(d[0],"droppable");f.greedyChild=(g=="isover"?1:0)}}if(f&&g=="isover"){f.isover=0;f.isout=1;f._out.call(f,c)}this[g]=1;this[g=="isout"?"isover":"isout"]=0;this[g=="isover"?"_over":"_out"].call(this,c);if(f&&g=="isout"){f.isout=0;f.isover=1;f._over.call(f,c)}})}}})(jQuery);;/*
- * jQuery UI Slider 1.7.1
- *
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI/Slider
- *
- * Depends:
- * ui.core.js
- */
(function(a){a.widget("ui.slider",a.extend({},a.ui.mouse,{_init:function(){var b=this,c=this.options;this._keySliding=false;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");this.range=a([]);if(c.range){if(c.range===true){this.range=a("<div></div>");if(!c.values){c.values=[this._valueMin(),this._valueMin()]}if(c.values.length&&c.values.length!=2){c.values=[c.values[0],c.values[0]]}}else{this.range=a("<div></div>")}this.range.appendTo(this.element).addClass("ui-slider-range");if(c.range=="min"||c.range=="max"){this.range.addClass("ui-slider-range-"+c.range)}this.range.addClass("ui-widget-header")}if(a(".ui-slider-handle",this.element).length==0){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}if(c.values&&c.values.length){while(a(".ui-slider-handle",this.element).length<c.values.length){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}}this.handles=a(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(d){d.preventDefault()}).hover(function(){a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){a(".ui-slider .ui-state-focus").removeClass("ui-state-focus");a(this).addClass("ui-state-focus")}).blur(function(){a(this).removeClass("ui-state-focus")});this.handles.each(function(d){a(this).data("index.ui-slider-handle",d)});this.handles.keydown(function(i){var f=true;var e=a(this).data("index.ui-slider-handle");if(b.options.disabled){return}switch(i.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:f=false;if(!b._keySliding){b._keySliding=true;a(this).addClass("ui-state-active");b._start(i,e)}break}var g,d,h=b._step();if(b.options.values&&b.options.values.length){g=d=b.values(e)}else{g=d=b.value()}switch(i.keyCode){case a.ui.keyCode.HOME:d=b._valueMin();break;case a.ui.keyCode.END:d=b._valueMax();break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g==b._valueMax()){return}d=g+h;break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g==b._valueMin()){return}d=g-h;break}b._slide(i,e,d);return f}).keyup(function(e){var d=a(this).data("index.ui-slider-handle");if(b._keySliding){b._stop(e,d);b._change(e,d);b._keySliding=false;a(this).removeClass("ui-state-active")}});this._refreshValue()},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy()},_mouseCapture:function(d){var e=this.options;if(e.disabled){return false}this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();var h={x:d.pageX,y:d.pageY};var j=this._normValueFromMouse(h);var c=this._valueMax()-this._valueMin()+1,f;var k=this,i;this.handles.each(function(l){var m=Math.abs(j-k.values(l));if(c>m){c=m;f=a(this);i=l}});if(e.range==true&&this.values(1)==e.min){f=a(this.handles[++i])}this._start(d,i);k._handleIndex=i;f.addClass("ui-state-active").focus();var g=f.offset();var b=!a(d.target).parents().andSelf().is(".ui-slider-handle");this._clickOffset=b?{left:0,top:0}:{left:d.pageX-g.left-(f.width()/2),top:d.pageY-g.top-(f.height()/2)-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};j=this._normValueFromMouse(h);this._slide(d,i,j);return true},_mouseStart:function(b){return true},_mouseDrag:function(d){var b={x:d.pageX,y:d.pageY};var c=this._normValueFromMouse(b);this._slide(d,this._handleIndex,c);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._handleIndex=null;this._clickOffset=null;return false},_detectOrientation:function(){this.orientation=this.options.orientation=="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(d){var c,h;if("horizontal"==this.orientation){c=this.elementSize.width;h=d.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{c=this.elementSize.height;h=d.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}var f=(h/c);if(f>1){f=1}if(f<0){f=0}if("vertical"==this.orientation){f=1-f}var e=this._valueMax()-this._valueMin(),i=f*e,b=i%this.options.step,g=this._valueMin()+i-b;if(b>(this.options.step/2)){g+=this.options.step}return parseFloat(g.toFixed(5))},_start:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("start",d,b)},_slide:function(f,e,d){var g=this.handles[e];if(this.options.values&&this.options.values.length){var b=this.values(e?0:1);if((e==0&&d>=b)||(e==1&&d<=b)){d=b}if(d!=this.values(e)){var c=this.values();c[e]=d;var h=this._trigger("slide",f,{handle:this.handles[e],value:d,values:c});var b=this.values(e?0:1);if(h!==false){this.values(e,d,(f.type=="mousedown"&&this.options.animate),true)}}}else{if(d!=this.value()){var h=this._trigger("slide",f,{handle:this.handles[e],value:d});if(h!==false){this._setData("value",d,(f.type=="mousedown"&&this.options.animate))}}}},_stop:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("stop",d,b)},_change:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("change",d,b)},value:function(b){if(arguments.length){this._setData("value",b);this._change(null,0)}return this._value()},values:function(b,e,c,d){if(arguments.length>1){this.options.values[b]=e;this._refreshValue(c);if(!d){this._change(null,b)}}if(arguments.length){if(this.options.values&&this.options.values.length){return this._values(b)}else{return this.value()}}else{return this._values()}},_setData:function(b,d,c){a.widget.prototype._setData.apply(this,arguments);switch(b){case"orientation":this._detectOrientation();this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue(c);break;case"value":this._refreshValue(c);break}},_step:function(){var b=this.options.step;return b},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_values:function(b){if(arguments.length){var c=this.options.values[b];if(c<this._valueMin()){c=this._valueMin()}if(c>this._valueMax()){c=this._valueMax()}return c}else{return this.options.values}},_valueMin:function(){var b=this.options.min;return b},_valueMax:function(){var b=this.options.max;return b},_refreshValue:function(c){var f=this.options.range,d=this.options,l=this;if(this.options.values&&this.options.values.length){var i,h;this.handles.each(function(p,n){var o=(l.values(p)-l._valueMin())/(l._valueMax()-l._valueMin())*100;var m={};m[l.orientation=="horizontal"?"left":"bottom"]=o+"%";a(this).stop(1,1)[c?"animate":"css"](m,d.animate);if(l.options.range===true){if(l.orientation=="horizontal"){(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({left:o+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({width:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}else{(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({bottom:(o)+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({height:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}}lastValPercent=o})}else{var j=this.value(),g=this._valueMin(),k=this._valueMax(),e=k!=g?(j-g)/(k-g)*100:0;var b={};b[l.orientation=="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[c?"animate":"css"](b,d.animate);(f=="min")&&(this.orientation=="horizontal")&&this.range.stop(1,1)[c?"animate":"css"]({width:e+"%"},d.animate);(f=="max")&&(this.orientation=="horizontal")&&this.range[c?"animate":"css"]({width:(100-e)+"%"},{queue:false,duration:d.animate});(f=="min")&&(this.orientation=="vertical")&&this.range.stop(1,1)[c?"animate":"css"]({height:e+"%"},d.animate);(f=="max")&&(this.orientation=="vertical")&&this.range[c?"animate":"css"]({height:(100-e)+"%"},{queue:false,duration:d.animate})}}}));a.extend(a.ui.slider,{getter:"value values",version:"1.7.1",eventPrefix:"slide",defaults:{animate:false,delay:0,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null}})})(jQuery);;
\ No newline at end of file
--- a/web/data/ui.tabs.css Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/* Caution! Ensure accessibility in print and other media types... */
-@media projection, screen { /* Use class for showing/hiding tab content, so that visibility can be better controlled in different media types... */
- .ui-tabs-hide {
- display: none;
- }
-}
-
-/* Hide useless elements in print layouts... */
-@media print {
- .ui-tabs-nav {
- display: none;
- }
-}
-
-/* Skin */
-.ui-tabs-nav, .ui-tabs-panel {
- font-family: "Trebuchet MS", Trebuchet, Verdana, Helvetica, Arial, sans-serif;
- font-size: 12px;
-
-}
-.ui-tabs-nav {
- list-style: none;
- margin: 0px;
- padding: 0px 0px 0px 4px;
-
-}
-.ui-tabs-nav:after { /* clearing without presentational markup, IE gets extra treatment */
- display: block;
- clear: both;
- content: " ";
-}
-.ui-tabs-nav li {
- float: left;
- margin: 0 0 0 1px;
- min-width: 84px; /* be nice to Opera */
- list-style: none;
- background: none;
- padding: 0px 0px 1px 1px;
-}
-.ui-tabs-nav a, .ui-tabs-nav a span {
- display: block;
- padding: 0 10px;
- background: url(tab.png) no-repeat;
-}
-.ui-tabs-nav a {
- margin: 1px 0 0; /* position: relative makes opacity fail for disabled tab in IE */
- padding-left: 0;
- color: #27537a;
- font-weight: bold;
- line-height: 1.2;
- text-align: center;
- text-decoration: none;
- white-space: nowrap; /* required in IE 6 */
- outline: 0; /* prevent dotted border in Firefox */
-}
-.ui-tabs-nav .ui-tabs-selected a {
- position: relative;
- top: 1px;
- z-index: 2;
- margin-top: 0;
- color: #000;
-}
-.ui-tabs-nav a span {
- width: 64px; /* IE 6 treats width as min-width */
- min-width: 64px;
- height: 18px; /* IE 6 treats height as min-height */
- min-height: 18px;
- padding-top: 6px;
- padding-right: 0;
-}
-*>.ui-tabs-nav a span { /* hide from IE 6 */
- width: auto;
- height: auto;
-}
-.ui-tabs-nav .ui-tabs-selected a span {
- padding-bottom: 1px;
-}
-.ui-tabs-nav .ui-tabs-selected a, .ui-tabs-nav a:hover, .ui-tabs-nav a:focus, .ui-tabs-nav a:active {
- background-position: 100% -150px;
-}
-.ui-tabs-nav a, .ui-tabs-nav .ui-tabs-disabled a:hover, .ui-tabs-nav .ui-tabs-disabled a:focus, .ui-tabs-nav .ui-tabs-disabled a:active {
- background-position: 100% -100px;
-}
-.ui-tabs-nav .ui-tabs-selected a span, .ui-tabs-nav a:hover span, .ui-tabs-nav a:focus span, .ui-tabs-nav a:active span {
- background-position: 0 -50px;
-}
-.ui-tabs-nav a span, .ui-tabs-nav .ui-tabs-disabled a:hover span, .ui-tabs-nav .ui-tabs-disabled a:focus span, .ui-tabs-nav .ui-tabs-disabled a:active span {
- background-position: 0 0;
-}
-.ui-tabs-nav .ui-tabs-selected a:link, .ui-tabs-nav .ui-tabs-selected a:visited, .ui-tabs-nav .ui-tabs-disabled a:link, .ui-tabs-nav .ui-tabs-disabled a:visited { /* @ Opera, use pseudo classes otherwise it confuses cursor... */
- cursor: text;
-}
-.ui-tabs-nav a:hover, .ui-tabs-nav a:focus, .ui-tabs-nav a:active,
-.ui-tabs-nav .ui-tabs-unselect a:hover, .ui-tabs-nav .ui-tabs-unselect a:focus, .ui-tabs-nav .ui-tabs-unselect a:active { /* @ Opera, we need to be explicit again here now... */
- cursor: pointer;
-}
-.ui-tabs-disabled {
- opacity: .4;
- filter: alpha(opacity=40);
-}
-.ui-tabs-panel {
- border-top: 1px solid #97a5b0;
- padding: 1em 8px;
- margin-top:-1px; /* Logilab style */
- background: #fff; /* declare background color for container to avoid distorted fonts in IE while fading */
-}
-.ui-tabs-loading em {
- padding: 0 0 0 20px;
- background: url(loading.gif) no-repeat 0 50%;
-}
-
-/* Additional IE specific bug fixes... */
-* html .ui-tabs-nav { /* auto clear, @ IE 6 & IE 7 Quirks Mode */
- display: inline-block;
-}
-*:first-child+html .ui-tabs-nav { /* @ IE 7 Standards Mode - do not group selectors, otherwise IE 6 will ignore complete rule (because of the unknown + combinator)... */
- display: inline-block;
-}
-
-/* ========= Lobilab styles =========== */
-
-/* added by katia */
-* html .ui-tabs-panel{
- width:100%;
-}
\ No newline at end of file
--- a/web/data/ui.tabs.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,587 +0,0 @@
-/*
- * jQuery UI Tabs @VERSION
- *
- * Copyright (c) 2007, 2010 Klaus Hartl (stilbuero.de)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI/Tabs
- *
- * Depends:
- * ui.core.js
- */
-(function($) {
-
-$.widget("ui.tabs", {
- _init: function() {
- this.options.event += '.tabs'; // namespace event
-
- // create tabs
- this._tabify(true);
- },
- _setData: function(key, value) {
- if ((/^selected/).test(key))
- this.select(value);
- else {
- this.options[key] = value;
- this._tabify();
- }
- },
- length: function() {
- return this.$tabs.length;
- },
- _tabId: function(a) {
- return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
- || this.options.idPrefix + $.data(a);
- },
- ui: function(tab, panel) {
- return {
- options: this.options,
- tab: tab,
- panel: panel,
- index: this.$tabs.index(tab)
- };
- },
- _tabify: function(init) {
-
- this.$lis = $('li:has(a[href])', this.element);
- this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
- this.$panels = $([]);
-
- var self = this, o = this.options;
-
- this.$tabs.each(function(i, a) {
- // inline tab
- if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
- self.$panels = self.$panels.add(a.hash);
- // remote tab
- else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
- $.data(a, 'href.tabs', a.href); // required for restore on destroy
- $.data(a, 'load.tabs', a.href); // mutable
- var id = self._tabId(a);
- a.href = '#' + id;
- var $panel = $('#' + id);
- if (!$panel.length) {
- $panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
- .insertAfter( self.$panels[i - 1] || self.element );
- $panel.data('destroy.tabs', true);
- }
- self.$panels = self.$panels.add( $panel );
- }
- // invalid tab href
- else
- o.disabled.push(i + 1);
- });
-
- // initialization from scratch
- if (init) {
-
- // attach necessary classes for styling if not present
- this.element.addClass(o.navClass);
- this.$panels.each(function() {
- var $this = $(this);
- $this.addClass(o.panelClass);
- });
-
- // Selected tab
- // use "selected" option or try to retrieve:
- // 1. from fragment identifier in url
- // 2. from cookie
- // 3. from selected class attribute on <li>
- if (o.selected === undefined) {
- if (location.hash) {
- this.$tabs.each(function(i, a) {
- if (a.hash == location.hash) {
- o.selected = i;
- // prevent page scroll to fragment
- if ($.browser.msie || $.browser.opera) { // && !o.remote
- var $toShow = $(location.hash), toShowId = $toShow.attr('id');
- $toShow.attr('id', '');
- setTimeout(function() {
- $toShow.attr('id', toShowId); // restore id
- }, 500);
- }
- scrollTo(0, 0);
- return false; // break
- }
- });
- }
- else if (o.cookie) {
- var index = parseInt($.cookie('ui-tabs-' + $.data(self.element[0])), 10);
- if (index && self.$tabs[index])
- o.selected = index;
- }
- else if (self.$lis.filter('.' + o.selectedClass).length)
- o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
- }
- o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default
-
- // Take disabling tabs via class attribute from HTML
- // into account and update option properly.
- // A selected tab cannot become disabled.
- o.disabled = $.unique(o.disabled.concat(
- $.map(this.$lis.filter('.' + o.disabledClass),
- function(n, i) { return self.$lis.index(n); } )
- )).sort();
- if ($.inArray(o.selected, o.disabled) != -1)
- o.disabled.splice($.inArray(o.selected, o.disabled), 1);
-
- // highlight selected tab
- this.$panels.addClass(o.hideClass);
- this.$lis.removeClass(o.selectedClass);
- if (o.selected !== null) {
- this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
- this.$lis.eq(o.selected).addClass(o.selectedClass);
-
- // seems to be expected behavior that the show callback is fired
- var onShow = function() {
- self._trigger('show', null,
- self.ui(self.$tabs[o.selected], self.$panels[o.selected]));
- };
-
- // load if remote tab
- if ($.data(this.$tabs[o.selected], 'load.tabs'))
- this.load(o.selected, onShow);
- // just trigger show event
- else
- onShow();
- }
-
- // clean up to avoid memory leaks in certain versions of IE 6
- $(window).bind('unload', function() {
- self.$tabs.unbind('.tabs');
- self.$lis = self.$tabs = self.$panels = null;
- });
-
- }
- // update selected after add/remove
- else
- o.selected = this.$lis.index( this.$lis.filter('.' + o.selectedClass)[0] );
-
- // set or update cookie after init and add/remove respectively
- if (o.cookie)
- $.cookie('ui-tabs-' + $.data(self.element[0]), o.selected, o.cookie);
-
- // disable tabs
- for (var i = 0, li; li = this.$lis[i]; i++)
- $(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);
-
- // reset cache if switching from cached to not cached
- if (o.cache === false)
- this.$tabs.removeData('cache.tabs');
-
- // set up animations
- var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
- if (o.fx && o.fx.constructor == Array)
- hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
- else
- hideFx = showFx = o.fx || baseFx;
-
- // reset some styles to maintain print style sheets etc.
- var resetCSS = { display: '', overflow: '', height: '' };
- if (!$.browser.msie) // not in IE to prevent ClearType font issue
- resetCSS.opacity = '';
-
- // Hide a tab, animation prevents browser scrolling to fragment,
- // $show is optional.
- function hideTab(clicked, $hide, $show) {
- $hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
- $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
- if ($.browser.msie && hideFx.opacity)
- $hide[0].style.filter = '';
- if ($show)
- showTab(clicked, $show, $hide);
- });
- }
-
- // Show a tab, animation prevents browser scrolling to fragment,
- // $hide is optional.
- function showTab(clicked, $show, $hide) {
- if (showFx === baseFx)
- $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
- $show.animate(showFx, showFx.duration || baseDuration, function() {
- $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
- if ($.browser.msie && showFx.opacity)
- $show[0].style.filter = '';
-
- // callback
- self._trigger('show', null, self.ui(clicked, $show[0]));
- });
- }
-
- // switch a tab
- function switchTab(clicked, $li, $hide, $show) {
- /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
- $.ajaxHistory.update(clicked.hash);
- }*/
- $li.addClass(o.selectedClass)
- .siblings().removeClass(o.selectedClass);
- hideTab(clicked, $hide, $show);
- }
-
- // attach tab event handler, unbind to avoid duplicates from former tabifying...
- this.$tabs.unbind('.tabs').bind(o.event, function() {
-
- //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
- var $li = $(this).parents('li:eq(0)'),
- $hide = self.$panels.filter(':visible'),
- $show = $(this.hash);
-
- // If tab is already selected and not unselectable or tab disabled or
- // or is already loading or click callback returns false stop here.
- // Check if click handler returns false last so that it is not executed
- // for a disabled or loading tab!
- if (($li.hasClass(o.selectedClass) && !o.unselect)
- || $li.hasClass(o.disabledClass)
- || $(this).hasClass(o.loadingClass)
- || self._trigger('select', null, self.ui(this, $show[0])) === false
- ) {
- this.blur();
- return false;
- }
-
- self.options.selected = self.$tabs.index(this);
-
- // if tab may be closed
- if (o.unselect) {
- if ($li.hasClass(o.selectedClass)) {
- self.options.selected = null;
- $li.removeClass(o.selectedClass);
- self.$panels.stop();
- hideTab(this, $hide);
- this.blur();
- return false;
- } else if (!$hide.length) {
- self.$panels.stop();
- var a = this;
- self.load(self.$tabs.index(this), function() {
- $li.addClass(o.selectedClass).addClass(o.unselectClass);
- showTab(a, $show);
- });
- this.blur();
- return false;
- }
- }
-
- if (o.cookie)
- $.cookie('ui-tabs-' + $.data(self.element[0]), self.options.selected, o.cookie);
-
- // stop possibly running animations
- self.$panels.stop();
-
- // show new tab
- if ($show.length) {
-
- // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
- /*if ($.browser.msie && o.bookmarkable) {
- var showId = this.hash.replace('#', '');
- $show.attr('id', '');
- setTimeout(function() {
- $show.attr('id', showId); // restore id
- }, 0);
- }*/
-
- var a = this;
- self.load(self.$tabs.index(this), $hide.length ?
- function() {
- switchTab(a, $li, $hide, $show);
- } :
- function() {
- $li.addClass(o.selectedClass);
- showTab(a, $show);
- }
- );
-
- // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
- /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
- var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
- setTimeout(function() {
- scrollTo(scrollX, scrollY);
- }, 0);*/
-
- } else
- throw 'jQuery UI Tabs: Mismatching fragment identifier.';
-
- // Prevent IE from keeping other link focussed when using the back button
- // and remove dotted border from clicked link. This is controlled in modern
- // browsers via CSS, also blur removes focus from address bar in Firefox
- // which can become a usability and annoying problem with tabsRotate.
- if ($.browser.msie)
- this.blur();
-
- //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
- return false;
-
- });
-
- // disable click if event is configured to something else
- if (!(/^click/).test(o.event))
- this.$tabs.bind('click.tabs', function() { return false; });
-
- },
- add: function(url, label, index) {
- if (index == undefined)
- index = this.$tabs.length; // append by default
-
- var o = this.options;
- var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
- $li.data('destroy.tabs', true);
-
- var id = url.indexOf('#') == 0 ? url.replace('#', '') : this._tabId( $('a:first-child', $li)[0] );
-
- // try to find an existing element before creating a new one
- var $panel = $('#' + id);
- if (!$panel.length) {
- $panel = $(o.panelTemplate).attr('id', id)
- .addClass(o.hideClass)
- .data('destroy.tabs', true);
- }
- $panel.addClass(o.panelClass);
- if (index >= this.$lis.length) {
- $li.appendTo(this.element);
- $panel.appendTo(this.element[0].parentNode);
- } else {
- $li.insertBefore(this.$lis[index]);
- $panel.insertBefore(this.$panels[index]);
- }
-
- o.disabled = $.map(o.disabled,
- function(n, i) { return n >= index ? ++n : n });
-
- this._tabify();
-
- if (this.$tabs.length == 1) {
- $li.addClass(o.selectedClass);
- $panel.removeClass(o.hideClass);
- var href = $.data(this.$tabs[0], 'load.tabs');
- if (href)
- this.load(index, href);
- }
-
- // callback
- this._trigger('add', null, this.ui(this.$tabs[index], this.$panels[index]));
- },
- remove: function(index) {
- var o = this.options, $li = this.$lis.eq(index).remove(),
- $panel = this.$panels.eq(index).remove();
-
- // If selected tab was removed focus tab to the right or
- // in case the last tab was removed the tab to the left.
- if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
- this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));
-
- o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
- function(n, i) { return n >= index ? --n : n });
-
- this._tabify();
-
- // callback
- this._trigger('remove', null, this.ui($li.find('a')[0], $panel[0]));
- },
- enable: function(index) {
- var o = this.options;
- if ($.inArray(index, o.disabled) == -1)
- return;
-
- var $li = this.$lis.eq(index).removeClass(o.disabledClass);
- if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
- $li.css('display', 'inline-block');
- setTimeout(function() {
- $li.css('display', 'block');
- }, 0);
- }
-
- o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
-
- // callback
- this._trigger('enable', null, this.ui(this.$tabs[index], this.$panels[index]));
- },
- disable: function(index) {
- var self = this, o = this.options;
- if (index != o.selected) { // cannot disable already selected tab
- this.$lis.eq(index).addClass(o.disabledClass);
-
- o.disabled.push(index);
- o.disabled.sort();
-
- // callback
- this._trigger('disable', null, this.ui(this.$tabs[index], this.$panels[index]));
- }
- },
- select: function(index) {
- if (typeof index == 'string')
- index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
- this.$tabs.eq(index).trigger(this.options.event);
- },
- load: function(index, callback) { // callback is for internal usage only
-
- var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
- bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');
-
- callback = callback || function() {};
-
- // no remote or from cache - just finish with callback
- if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
- callback();
- return;
- }
-
- // load remote from here on
-
- var inner = function(parent) {
- var $parent = $(parent), $inner = $parent.find('*:last');
- return $inner.length && $inner.is(':not(img)') && $inner || $parent;
- };
- var cleanup = function() {
- self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
- .each(function() {
- if (o.spinner)
- inner(this).parent().html(inner(this).data('label.tabs'));
- });
- self.xhr = null;
- };
-
- if (o.spinner) {
- var label = inner(a).html();
- inner(a).wrapInner('<em></em>')
- .find('em').data('label.tabs', label).html(o.spinner);
- }
-
- var ajaxOptions = $.extend({}, o.ajaxOptions, {
- url: url,
- success: function(r, s) {
- $(a.hash).html(r);
- cleanup();
-
- if (o.cache)
- $.data(a, 'cache.tabs', true); // if loaded once do not load them again
-
- // callbacks
- self._trigger('load', null, self.ui(self.$tabs[index], self.$panels[index]));
- o.ajaxOptions.success && o.ajaxOptions.success(r, s);
-
- // This callback is required because the switch has to take
- // place after loading has completed. Call last in order to
- // fire load before show callback...
- callback();
- }
- });
- if (this.xhr) {
- // terminate pending requests from other tabs and restore tab label
- this.xhr.abort();
- cleanup();
- }
- $a.addClass(o.loadingClass);
- setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
- self.xhr = $.ajax(ajaxOptions);
- }, 0);
-
- },
- url: function(index, url) {
- this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
- },
- destroy: function() {
- var o = this.options;
- this.element.unbind('.tabs')
- .removeClass(o.navClass).removeData('tabs');
- this.$tabs.each(function() {
- var href = $.data(this, 'href.tabs');
- if (href)
- this.href = href;
- var $this = $(this).unbind('.tabs');
- $.each(['href', 'load', 'cache'], function(i, prefix) {
- $this.removeData(prefix + '.tabs');
- });
- });
- this.$lis.add(this.$panels).each(function() {
- if ($.data(this, 'destroy.tabs'))
- $(this).remove();
- else
- $(this).removeClass([o.selectedClass, o.unselectClass,
- o.disabledClass, o.panelClass, o.hideClass].join(' '));
- });
- }
-});
-
-$.ui.tabs.defaults = {
- // basic setup
- unselect: false,
- event: 'click',
- disabled: [],
- cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
- // TODO history: false,
-
- // Ajax
- spinner: 'Loading…',
- cache: false,
- idPrefix: 'ui-tabs-',
- ajaxOptions: {},
-
- // animations
- fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
-
- // templates
- tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
- panelTemplate: '<div></div>',
-
- // CSS classes
- navClass: 'ui-tabs-nav',
- selectedClass: 'ui-tabs-selected',
- unselectClass: 'ui-tabs-unselect',
- disabledClass: 'ui-tabs-disabled',
- panelClass: 'ui-tabs-panel',
- hideClass: 'ui-tabs-hide',
- loadingClass: 'ui-tabs-loading'
-};
-
-$.ui.tabs.getter = "length";
-
-/*
- * Tabs Extensions
- */
-
-/*
- * Rotate
- */
-$.extend($.ui.tabs.prototype, {
- rotation: null,
- rotate: function(ms, continuing) {
-
- continuing = continuing || false;
-
- var self = this, t = this.options.selected;
-
- function start() {
- self.rotation = setInterval(function() {
- t = ++t < self.$tabs.length ? t : 0;
- self.select(t);
- }, ms);
- }
-
- function stop(e) {
- if (!e || e.clientX) { // only in case of a true click
- clearInterval(self.rotation);
- }
- }
-
- // start interval
- if (ms) {
- start();
- if (!continuing)
- this.$tabs.bind(this.options.event, stop);
- else
- this.$tabs.bind(this.options.event, function() {
- stop();
- t = self.options.selected;
- start();
- });
- }
- // stop interval
- else {
- stop();
- this.$tabs.unbind(this.options.event, stop);
- }
- }
-});
-
-})(jQuery);
--- a/web/facet.py Mon May 16 16:24:00 2011 +0200
+++ b/web/facet.py Wed Jul 20 18:21:47 2011 +0200
@@ -78,7 +78,7 @@
def prepare_facets_rqlst(rqlst, args=None):
"""prepare a syntax tree to generate facet filters
- * remove ORDERBY clause
+ * remove ORDERBY/GROUPBY clauses
* cleanup selection (remove everything)
* undefine unnecessary variables
* set DISTINCT
@@ -91,8 +91,10 @@
select.set_limit(None)
select.set_offset(None)
baserql = select.as_string(kwargs=args)
- # cleanup sort terms
+ # cleanup sort terms / group by
select.remove_sort_terms()
+ select.remove_groups()
+ # XXX remove aggregat from having
# selection: only vocabulary entity
for term in select.selection[:]:
select.remove_selected(term)
@@ -605,7 +607,7 @@
insert_attr_select_relation(
rqlst, self.filtered_variable, self.rtype, self.role, self.target_attr,
select_target_entity=False)
- values = [str(x) for x, in self.rqlexec(rqlst.as_string())]
+ values = [unicode(x) for x, in self.rqlexec(rqlst.as_string())]
except:
self.exception('while computing values for %s', self)
return []
@@ -936,7 +938,7 @@
"""return the widget instance to use to display this facet"""
values = set(value for _, value in self.vocabulary() if value is not None)
# Rset with entities (the facet is selected) but without values
- if len(values) == 0:
+ if len(values) < 2:
return None
return self.wdgclass(self, min(values), max(values))
@@ -1016,6 +1018,7 @@
rtype = 'has_image'
role = 'subject'
"""
+ __select__ = partial_relation_possible() & match_context_prop()
rtype = None # override me in subclass
role = 'subject' # role of filtered entity in the relation
@@ -1125,8 +1128,8 @@
def _render(self):
facet = self.facet
- facet._cw.add_js('ui.slider.js')
- facet._cw.add_css('ui.all.css')
+ facet._cw.add_js('jquery.ui.js')
+ facet._cw.add_css('jquery.ui.css')
sliderid = make_uid('theslider')
facetid = xml_escape(self.facet.__regid__)
facet._cw.html_headers.add_onload(self.onload % {
--- a/web/formfields.py Mon May 16 16:24:00 2011 +0200
+++ b/web/formfields.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -386,12 +386,11 @@
"""
assert self.choices is not None
if callable(self.choices):
- try:
- if getattr(self.choices, 'im_self', None) is self:
- vocab = self.choices(form=form, **kwargs)
- else:
- vocab = self.choices(form=form, field=self, **kwargs)
- except TypeError:
+ if getattr(self.choices, 'im_self', None) is self:
+ vocab = self.choices(form=form, **kwargs)
+ elif support_args(self.choices, 'form', 'field'):
+ vocab = self.choices(form=form, field=self, **kwargs)
+ else:
try:
vocab = self.choices(form=form, **kwargs)
warn('[3.6] %s: choices should now take '
@@ -875,7 +874,9 @@
if self.choices:
return super(BooleanField, self).vocabulary(form)
if self.allow_none:
- return [('', ''), (form._cw._('yes'), '1'), (form._cw._('no'), '0')]
+ return [(form._cw._('indifferent'), ''),
+ (form._cw._('yes'), '1'),
+ (form._cw._('no'), '0')]
# XXX empty string for 'no' in that case for bw compat
return [(form._cw._('yes'), '1'), (form._cw._('no'), '')]
@@ -1200,14 +1201,19 @@
FIELDS = {
- 'Boolean': BooleanField,
+ 'String' : StringField,
'Bytes': FileField,
- 'Date': DateField,
- 'Datetime': DateTimeField,
+ 'Password': PasswordField,
+
+ 'Boolean': BooleanField,
'Int': IntField,
'Float': FloatField,
'Decimal': StringField,
- 'Password': PasswordField,
- 'String' : StringField,
- 'Time': TimeField,
+
+ 'Date': DateField,
+ 'Datetime': DateTimeField,
+ 'TZDatetime': DateTimeField,
+ 'Time': TimeField,
+ 'TZTime': TimeField,
+ # XXX implement 'Interval': TimeIntervalField,
}
--- a/web/formwidgets.py Mon May 16 16:24:00 2011 +0200
+++ b/web/formwidgets.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -435,6 +435,7 @@
an unicode string, or a list of unicode strings.
"""
vocabulary_widget = True
+ default_size = 5
def __init__(self, attrs=None, multiple=False, **kwargs):
super(Select, self).__init__(attrs, **kwargs)
@@ -442,11 +443,10 @@
def _render(self, form, field, renderer):
curvalues, attrs = self.values_and_attributes(form, field)
- if not 'size' in attrs:
- attrs['size'] = self._multiple and '5' or '1'
options = []
optgroup_opened = False
- for option in field.vocabulary(form):
+ vocab = field.vocabulary(form)
+ for option in vocab:
try:
label, value, oattrs = option
except ValueError:
@@ -466,6 +466,12 @@
options.append(tags.option(label, value=value, **oattrs))
if optgroup_opened:
options.append(u'</optgroup>')
+ if not 'size' in attrs:
+ if self._multiple:
+ size = unicode(min(self.default_size, len(vocab) or 1))
+ else:
+ size = u'1'
+ attrs['size'] = size
return tags.select(name=field.input_name(form, self.suffix),
multiple=self._multiple, options=options, **attrs)
@@ -478,11 +484,12 @@
default <br/> is used.
"""
type = 'checkbox'
+ default_separator = u'<br/>\n'
vocabulary_widget = True
- def __init__(self, attrs=None, separator=u'<br/>\n', **kwargs):
+ def __init__(self, attrs=None, separator=None, **kwargs):
super(CheckBox, self).__init__(attrs, **kwargs)
- self.separator = separator
+ self.separator = separator or self.default_separator
def _render(self, form, field, renderer):
curvalues, attrs = self.values_and_attributes(form, field)
--- a/web/request.py Mon May 16 16:24:00 2011 +0200
+++ b/web/request.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -116,8 +116,8 @@
pid = self.form.get('pageid')
if pid is None:
pid = make_uid(id(self))
+ self.html_headers.define_var('pageid', pid, override=False)
self.pageid = pid
- self.html_headers.define_var('pageid', pid, override=False)
@property
def authmode(self):
@@ -616,8 +616,10 @@
extraparams.setdefault('fname', 'view')
url = self.build_url('json', **extraparams)
cbname = build_cb_uid(url[:50])
+ # think to propagate pageid. XXX see https://www.cubicweb.org/ticket/1753121
jscode = 'function %s() { $("#%s").%s; }' % (
- cbname, nodeid, js.loadxhtml(url, None, 'get', replacemode))
+ cbname, nodeid, js.loadxhtml(url, {'pageid': self.pageid},
+ 'get', replacemode))
self.html_headers.add_post_inline_script(jscode)
return "javascript: %s()" % cbname
@@ -732,26 +734,14 @@
return None, None
def parse_accept_header(self, header):
- """returns an ordered list of preferred languages"""
+ """returns an ordered list of accepted values"""
+ try:
+ value_parser, value_sort_key = ACCEPT_HEADER_PARSER[header.lower()]
+ except KeyError:
+ value_parser = value_sort_key = None
accepteds = self.get_header(header, '')
- values = []
- for info in accepteds.split(','):
- try:
- value, scores = info.split(';', 1)
- except ValueError:
- value = info
- score = 1.0
- else:
- for score in scores.split(';'):
- try:
- scorekey, scoreval = score.split('=')
- if scorekey == 'q': # XXX 'level'
- score = float(scoreval)
- except ValueError:
- continue
- values.append((score, value))
- values.sort(reverse=True)
- return (value for (score, value) in values)
+ values = _parse_accept_header(accepteds, value_parser, value_sort_key)
+ return (raw_value for (raw_value, parsed_value, score) in values)
def header_if_modified_since(self):
"""If the HTTP header If-modified-since is set, return the equivalent
@@ -766,8 +756,16 @@
will display '<[' at the beginning of the page
"""
self.set_content_type('text/html')
- self.main_stream.doctype = TRANSITIONAL_DOCTYPE_NOEXT
- self.main_stream.xmldecl = u''
+ self.main_stream.set_doctype(TRANSITIONAL_DOCTYPE_NOEXT)
+
+ def set_doctype(self, doctype, reset_xmldecl=True):
+ """helper method to dynamically change page doctype
+
+ :param doctype: the new doctype, e.g. '<!DOCTYPE html>'
+ :param reset_xmldecl: if True, remove the '<?xml version="1.0"?>'
+ declaration from the page
+ """
+ self.main_stream.set_doctype(doctype, reset_xmldecl)
# page data management ####################################################
@@ -856,5 +854,91 @@
self.parse_accept_header('Accept-Language')]
+
+## HTTP-accept parsers / utilies ##############################################
+def _mimetype_sort_key(accept_info):
+ """accepted mimetypes must be sorted by :
+
+ 1/ highest score first
+ 2/ most specific mimetype first, e.g. :
+ - 'text/html level=1' is more specific 'text/html'
+ - 'text/html' is more specific than 'text/*'
+ - 'text/*' itself more specific than '*/*'
+
+ """
+ raw_value, (media_type, media_subtype, media_type_params), score = accept_info
+ # FIXME: handle '+' in media_subtype ? (should xhtml+xml have a
+ # higher precedence than xml ?)
+ if media_subtype == '*':
+ score -= 0.0001
+ if media_type == '*':
+ score -= 0.0001
+ return 1./score, media_type, media_subtype, 1./(1+len(media_type_params))
+
+def _charset_sort_key(accept_info):
+ """accepted mimetypes must be sorted by :
+
+ 1/ highest score first
+ 2/ most specific charset first, e.g. :
+ - 'utf-8' is more specific than '*'
+ """
+ raw_value, value, score = accept_info
+ if value == '*':
+ score -= 0.0001
+ return 1./score, value
+
+def _parse_accept_header(raw_header, value_parser=None, value_sort_key=None):
+ """returns an ordered list accepted types
+
+ returned value is a list of 2-tuple (value, score), ordered
+ by score. Exact type of `value` will depend on what `value_parser`
+ will reutrn. if `value_parser` is None, then the raw value, as found
+ in the http header, is used.
+ """
+ if value_sort_key is None:
+ value_sort_key = lambda infos: 1./infos[-1]
+ values = []
+ for info in raw_header.split(','):
+ score = 1.0
+ other_params = {}
+ try:
+ value, infodef = info.split(';', 1)
+ except ValueError:
+ value = info
+ else:
+ for info in infodef.split(';'):
+ try:
+ infokey, infoval = info.split('=')
+ if infokey == 'q': # XXX 'level'
+ score = float(infoval)
+ continue
+ except ValueError:
+ continue
+ other_params[infokey] = infoval
+ parsed_value = value_parser(value, other_params) if value_parser else value
+ values.append( (value.strip(), parsed_value, score) )
+ values.sort(key=value_sort_key)
+ return values
+
+
+def _mimetype_parser(value, other_params):
+ """return a 3-tuple
+ (type, subtype, type_params) corresponding to the mimetype definition
+ e.g. : for 'text/*', `mimetypeinfo` will be ('text', '*', {}), for
+ 'text/html;level=1', `mimetypeinfo` will be ('text', '*', {'level': '1'})
+ """
+ try:
+ media_type, media_subtype = value.strip().split('/')
+ except ValueError: # safety belt : '/' should always be present
+ media_type = value.strip()
+ media_subtype = '*'
+ return (media_type, media_subtype, other_params)
+
+
+ACCEPT_HEADER_PARSER = {
+ 'accept': (_mimetype_parser, _mimetype_sort_key),
+ 'accept-charset': (None, _charset_sort_key),
+ }
+
from cubicweb import set_log_methods
set_log_methods(CubicWebRequestBase, LOGGER)
--- a/web/schemaviewer.py Mon May 16 16:24:00 2011 +0200
+++ b/web/schemaviewer.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -15,9 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""an helper class to display CubicWeb schema using ureports
+"""an helper class to display CubicWeb schema using ureports"""
-"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -152,9 +151,9 @@
continue
label = rschema.type
if role == 'subject':
- cards = rschema.rproperty(eschema, oeschema, 'cardinality')
+ cards = rschema.rdef(eschema, oeschema).cardinality
else:
- cards = rschema.rproperty(oeschema, eschema, 'cardinality')
+ cards = rschema.rdef(oeschema, eschema).cardinality
cards = cards[::-1]
label = '%s %s %s' % (CARD_MAP[cards[1]], label,
CARD_MAP[cards[0]])
@@ -217,7 +216,7 @@
if val is None:
val = ''
elif prop == 'constraints':
- val = ', '.join([c.restriction for c in val])
+ val = ', '.join([c.expression for c in val])
elif isinstance(val, dict):
for key, value in val.iteritems():
if isinstance(value, (list, tuple)):
--- a/web/test/jstests/ajax_url0.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-<div id="ajaxroot">
- <h1>Hello</h1>
-</div>
--- a/web/test/jstests/ajax_url1.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-<div id="ajaxroot">
- <div class="ajaxHtmlHead">
- <script src="http://foo.js" type="text/javascript"> </script>
- </div>
- <h1>Hello</h1>
-</div>
--- a/web/test/jstests/ajax_url2.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<div id="ajaxroot">
- <div class="ajaxHtmlHead">
- <script src="http://foo.js" type="text/javascript"> </script>
- <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
- </div>
- <h1>Hello</h1>
-</div>
--- a/web/test/jstests/ajaxresult.json Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-['foo', 'bar']
--- a/web/test/jstests/test_ajax.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-<html>
- <head>
- <!-- dependencies -->
- <script type="text/javascript" src="../../data/jquery.js"></script>
- <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.dom.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.htmlhelpers.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.ajax.js" type="text/javascript"></script>
- <!-- qunit files -->
- <script type="text/javascript" src="../../../devtools/data/qunit.js"></script>
- <link rel="stylesheet" type="text/css" media="all" href="../../../devtools/data/qunit.css" />
- <!-- test suite -->
- <script src="cwmock.js" type="text/javascript"></script>
- <script src="test_ajax.js" type="text/javascript"></script>
- </head>
- <body>
- <div id="main"> </div>
- <h1 id="qunit-header">cubicweb.ajax.js functions tests</h1>
- <h2 id="qunit-banner"></h2>
- <ol id="qunit-tests">
- </body>
-</html>
--- a/web/test/jstests/test_ajax.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,244 +0,0 @@
-$(document).ready(function() {
-
- module("ajax", {
- setup: function() {
- this.scriptsLength = $('head script[src]').length-1;
- this.cssLength = $('head link[rel=stylesheet]').length-1;
- // re-initialize cw loaded cache so that each tests run in a
- // clean environment, have a lookt at _loadAjaxHtmlHead implementation
- // in cubicweb.ajax.js for more information.
- cw.loaded_src = [];
- cw.loaded_href = [];
- },
- teardown: function() {
- $('head script[src]:gt(' + this.scriptsLength + ')').remove();
- $('head link[rel=stylesheet]:gt(' + this.cssLength + ')').remove();
- }
- });
-
- function jsSources() {
- return $.map($('head script[src]'), function(script) {
- return script.getAttribute('src');
- });
- }
-
- test('test simple h1 inclusion (ajax_url0.html)', function() {
- expect(3);
- equals(jQuery('#main').children().length, 0);
- stop();
- jQuery('#main').loadxhtml('/../ajax_url0.html', {
- callback: function() {
- equals(jQuery('#main').children().length, 1);
- equals(jQuery('#main h1').html(), 'Hello');
- start();
- }
- });
- });
-
- test('test simple html head inclusion (ajax_url1.html)', function() {
- expect(6);
- var scriptsIncluded = jsSources();
- equals(jQuery.inArray('http://foo.js', scriptsIncluded), - 1);
- stop();
- jQuery('#main').loadxhtml('/../ajax_url1.html', {
- callback: function() {
- var origLength = scriptsIncluded.length;
- scriptsIncluded = jsSources();
- // check that foo.js has been *appended* to <head>
- equals(scriptsIncluded.length, origLength + 1);
- equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
- // check that <div class="ajaxHtmlHead"> has been removed
- equals(jQuery('#main').children().length, 1);
- equals(jQuery('div.ajaxHtmlHead').length, 0);
- equals(jQuery('#main h1').html(), 'Hello');
- start();
- }
- });
- });
-
- test('test addCallback', function() {
- expect(3);
- equals(jQuery('#main').children().length, 0);
- stop();
- var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
- d.addCallback(function() {
- equals(jQuery('#main').children().length, 1);
- equals(jQuery('#main h1').html(), 'Hello');
- start();
- });
- });
-
- test('test callback after synchronous request', function() {
- expect(1);
- var deferred = new Deferred();
- var result = jQuery.ajax({
- url: './ajax_url0.html',
- async: false,
- beforeSend: function(xhr) {
- deferred._req = xhr;
- },
- success: function(data, status) {
- deferred.success(data);
- }
- });
- stop();
- deferred.addCallback(function() {
- // add an assertion to ensure the callback is executed
- ok(true, "callback is executed");
- start();
- });
- });
-
- test('test addCallback with parameters', function() {
- expect(3);
- equals(jQuery('#main').children().length, 0);
- stop();
- var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
- d.addCallback(function(data, req, arg1, arg2) {
- equals(arg1, 'Hello');
- equals(arg2, 'world');
- start();
- },
- 'Hello', 'world');
- });
-
- test('test callback after synchronous request with parameters', function() {
- var deferred = new Deferred();
- var result = jQuery.ajax({
- url: '/../ajax_url0.html',
- async: false,
- beforeSend: function(xhr) {
- deferred._req = xhr;
- },
- success: function(data, status) {
- deferred.success(data);
- }
- });
- deferred.addCallback(function(data, req, arg1, arg2) {
- // add an assertion to ensure the callback is executed
- ok(true, "callback is executed");
- equals(arg1, 'Hello');
- equals(arg2, 'world');
- },
- 'Hello', 'world');
- });
-
- test('test addErrback', function() {
- expect(1);
- stop();
- var d = jQuery('#main').loadxhtml('/../ajax_url0.html');
- d.addCallback(function() {
- // throw an exception to start errback chain
- throw new Error();
- });
- d.addErrback(function() {
- ok(true, "errback is executed");
- start();
- });
- });
-
- test('test callback / errback execution order', function() {
- expect(4);
- var counter = 0;
- stop();
- var d = jQuery('#main').loadxhtml('/../ajax_url0.html', {
- callback: function() {
- equals(++counter, 1); // should be executed first
- start();
- }
- });
- d.addCallback(function() {
- equals(++counter, 2); // should be executed and break callback chain
- throw new Error();
- });
- d.addCallback(function() {
- // should not be executed since second callback raised an error
- ok(false, "callback is executed");
- });
- d.addErrback(function() {
- // should be executed after the second callback
- equals(++counter, 3);
- });
- d.addErrback(function() {
- // should be executed after the first errback
- equals(++counter, 4);
- });
- });
-
- test('test already included resources are ignored (ajax_url1.html)', function() {
- expect(10);
- var scriptsIncluded = jsSources();
- // NOTE:
- equals(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
- equals(jQuery('head link').length, 1);
- /* use endswith because in pytest context we have an absolute path */
- ok(jQuery('head link').attr('href').endswith('/qunit.css'));
- stop();
- jQuery('#main').loadxhtml('/../ajax_url1.html', {
- callback: function() {
- var origLength = scriptsIncluded.length;
- scriptsIncluded = jsSources();
- try {
- // check that foo.js has been inserted in <head>
- equals(scriptsIncluded.length, origLength + 1);
- equals(scriptsIncluded[origLength].indexOf('http://foo.js'), 0);
- // check that <div class="ajaxHtmlHead"> has been removed
- equals(jQuery('#main').children().length, 1);
- equals(jQuery('div.ajaxHtmlHead').length, 0);
- equals(jQuery('#main h1').html(), 'Hello');
- // qunit.css is not added twice
- equals(jQuery('head link').length, 1);
- /* use endswith because in pytest context we have an absolute path */
- ok(jQuery('head link').attr('href').endswith('/qunit.css'));
- } finally {
- start();
- }
- }
- });
- });
-
- test('test synchronous request loadRemote', function() {
- var res = loadRemote('/../ajaxresult.json', {},
- 'GET', true);
- same(res, ['foo', 'bar']);
- });
-
- test('test event on CubicWeb', function() {
- expect(1);
- stop();
- var events = null;
- jQuery(CubicWeb).bind('server-response', function() {
- // check that server-response event on CubicWeb is triggered
- events = 'CubicWeb';
- });
- jQuery('#main').loadxhtml('/../ajax_url0.html', {
- callback: function() {
- equals(events, 'CubicWeb');
- start();
- }
- });
- });
-
- test('test event on node', function() {
- expect(3);
- stop();
- var nodes = [];
- jQuery('#main').bind('server-response', function() {
- nodes.push('node');
- });
- jQuery(CubicWeb).bind('server-response', function() {
- nodes.push('CubicWeb');
- });
- jQuery('#main').loadxhtml('/../ajax_url0.html', {
- callback: function() {
- equals(nodes.length, 2);
- // check that server-response event on CubicWeb is triggered
- // only once and event server-response on node is triggered
- equals(nodes[0], 'CubicWeb');
- equals(nodes[1], 'node');
- start();
- }
- });
- });
-});
-
--- a/web/test/jstests/test_htmlhelpers.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-<html>
- <head>
- <script type="text/javascript" src="../../data/jquery.js"></script>
- <script src="../../data/cubicweb.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.htmlhelpers.js" type="text/javascript"></script>
- <script type="text/javascript" src="qunit.js"></script>
- <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
- <script src="cwmock.js" type="text/javascript"></script>
- <script src="test_htmlhelpers.js" type="text/javascript"></script>
- </head>
- <body>
- <div id="main">
- </div>
- <h1 id="qunit-header">cubicweb.htmlhelpers.js functions tests</h1>
- <h2 id="qunit-banner"></h2>
- <h2 id="qunit-userAgent"></h2>
- <ol id="qunit-tests">
- </body>
-</html>
--- a/web/test/jstests/test_htmlhelpers.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-$(document).ready(function() {
-
- module("module2", {
- setup: function() {
- $('#main').append('<select id="theselect" multiple="multiple" size="2">' +
- '</select>');
- }
- });
-
- test("test first selected", function() {
- $('#theselect').append('<option value="foo">foo</option>' +
- '<option selected="selected" value="bar">bar</option>' +
- '<option value="baz">baz</option>' +
- '<option selected="selecetd"value="spam">spam</option>');
- var selected = firstSelected(document.getElementById("theselect"));
- equals(selected.value, 'bar');
- });
-
- test("test first selected 2", function() {
- $('#theselect').append('<option value="foo">foo</option>' +
- '<option value="bar">bar</option>' +
- '<option value="baz">baz</option>' +
- '<option value="spam">spam</option>');
- var selected = firstSelected(document.getElementById("theselect"));
- equals(selected, null);
- });
-
- module("visibilty");
- test('toggleVisibility', function() {
- $('#main').append('<div id="foo"></div>');
- toggleVisibility('foo');
- ok($('#foo').hasClass('hidden'), 'check hidden class is set');
- });
-
-});
-
--- a/web/test/jstests/test_utils.html Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<html>
- <head>
- <script type="text/javascript" src="../../data/jquery.js"></script>
- <script src="../../data/jquery.corner.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.python.js" type="text/javascript"></script>
- <script src="../../data/cubicweb.compat.js" type="text/javascript"></script>
- <script src="utils.js" type="text/javascript"></script>
- <script type="text/javascript" src="qunit.js"></script>
- <link rel="stylesheet" type="text/css" media="all" href="qunit.css" />
- <script src="cwmock.js" type="text/javascript"></script>
- <script src="test_utils.js" type="text/javascript"></script>
- </head>
- <body>
- <div id="main">
- </div>
- <h1 id="qunit-header">cw.utils functions tests</h1>
- <h2 id="qunit-banner"></h2>
- <h2 id="qunit-userAgent"></h2>
- <ol id="qunit-tests">
- </body>
-</html>
--- a/web/test/jstests/test_utils.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-$(document).ready(function() {
-
- module("datetime");
-
- test("test full datetime", function() {
- equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18, 10, 30, 0, 0)),
- '1986-04-18 10:30:00');
- });
-
- test("test only date", function() {
- equals(cw.utils.toISOTimestamp(new Date(1986, 3, 18)), '1986-04-18 00:00:00');
- });
-
- test("test null", function() {
- equals(cw.utils.toISOTimestamp(null), null);
- });
-
- module("parsing");
- test("test basic number parsing", function() {
- var d = strptime('2008/08/08', '%Y/%m/%d');
- same(datetuple(d), [2008, 8, 8, 0, 0]);
- d = strptime('2008/8/8', '%Y/%m/%d');
- same(datetuple(d), [2008, 8, 8, 0, 0]);
- d = strptime('8/8/8', '%Y/%m/%d');
- same(datetuple(d), [8, 8, 8, 0, 0]);
- d = strptime('0/8/8', '%Y/%m/%d');
- same(datetuple(d), [0, 8, 8, 0, 0]);
- d = strptime('-10/8/8', '%Y/%m/%d');
- same(datetuple(d), [-10, 8, 8, 0, 0]);
- d = strptime('-35000', '%Y');
- same(datetuple(d), [-35000, 1, 1, 0, 0]);
- });
-
- test("test custom format parsing", function() {
- var d = strptime('2008-08-08', '%Y-%m-%d');
- same(datetuple(d), [2008, 8, 8, 0, 0]);
- d = strptime('2008 - ! 08: 08', '%Y - ! %m: %d');
- same(datetuple(d), [2008, 8, 8, 0, 0]);
- d = strptime('2008-08-08 12:14', '%Y-%m-%d %H:%M');
- same(datetuple(d), [2008, 8, 8, 12, 14]);
- d = strptime('2008-08-08 1:14', '%Y-%m-%d %H:%M');
- same(datetuple(d), [2008, 8, 8, 1, 14]);
- d = strptime('2008-08-08 01:14', '%Y-%m-%d %H:%M');
- same(datetuple(d), [2008, 8, 8, 1, 14]);
- });
-
- module("sliceList");
- test("test slicelist", function() {
- var list = ['a', 'b', 'c', 'd', 'e', 'f'];
- same(sliceList(list, 2), ['c', 'd', 'e', 'f']);
- same(sliceList(list, 2, -2), ['c', 'd']);
- same(sliceList(list, -3), ['d', 'e', 'f']);
- same(sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
- same(sliceList(list), list);
- });
-
- module("formContents", {
- setup: function() {
- $('#main').append('<form id="test-form"></form>');
- }
- });
- // XXX test fckeditor
- test("test formContents", function() {
- $('#test-form').append('<input name="input-text" ' +
- 'type="text" value="toto" />');
- $('#test-form').append('<textarea rows="10" cols="30" '+
- 'name="mytextarea">Hello World!</textarea> ');
- $('#test-form').append('<input name="choice" type="radio" ' +
- 'value="yes" />');
- $('#test-form').append('<input name="choice" type="radio" ' +
- 'value="no" checked="checked"/>');
- $('#test-form').append('<input name="check" type="checkbox" ' +
- 'value="yes" />');
- $('#test-form').append('<input name="check" type="checkbox" ' +
- 'value="no" checked="checked"/>');
- $('#test-form').append('<select id="theselect" name="theselect" ' +
- 'multiple="multiple" size="2"></select>');
- $('#theselect').append('<option selected="selected" ' +
- 'value="foo">foo</option>' +
- '<option value="bar">bar</option>');
- //Append an unchecked radio input : should not be in formContents list
- $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
- 'value="one" />');
- $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
- 'value="two"/>');
- same(formContents($('#test-form')[0]), [
- ['input-text', 'mytextarea', 'choice', 'check', 'theselect'],
- ['toto', 'Hello World!', 'no', 'no', 'foo']
- ]);
- });
-});
-
--- a/web/test/jstests/utils.js Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-function datetuple(d) {
- return [d.getFullYear(), d.getMonth()+1, d.getDate(),
- d.getHours(), d.getMinutes()];
-}
-
-function pprint(obj) {
- print('{');
- for(k in obj) {
- print(' ' + k + ' = ' + obj[k]);
- }
- print('}');
-}
-
-function arrayrepr(array) {
- return '[' + array.join(', ') + ']';
-}
-
-function assertArrayEquals(array1, array2) {
- if (array1.length != array2.length) {
- throw new crosscheck.AssertionFailure(array1.join(', ') + ' != ' + array2.join(', '));
- }
- for (var i=0; i<array1.length; i++) {
- if (array1[i] != array2[i]) {
-
- throw new crosscheck.AssertionFailure(arrayrepr(array1) + ' and ' + arrayrepr(array2)
- + ' differs at index ' + i);
- }
- }
-}
--- a/web/test/test_jscript.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-from cubicweb.devtools.qunit import QUnitTestCase, unittest_main
-
-from os import path as osp
-
-
-class JScript(QUnitTestCase):
-
- all_js_tests = (
- ("jstests/test_utils.js", (
- "../data/cubicweb.js",
- "../data/cubicweb.compat.js",
- "../data/cubicweb.python.js",
- "jstests/utils.js",
- ),
- ),
-
- ("jstests/test_htmlhelpers.js", (
- "../data/cubicweb.js",
- "../data/cubicweb.compat.js",
- "../data/cubicweb.python.js",
- "../data/cubicweb.htmlhelpers.js",
- ),
- ),
-
- ("jstests/test_ajax.js", (
- "../data/cubicweb.python.js",
- "../data/cubicweb.js",
- "../data/cubicweb.compat.js",
- "../data/cubicweb.htmlhelpers.js",
- "../data/cubicweb.ajax.js",
- ), (
- "jstests/ajax_url0.html",
- "jstests/ajax_url1.html",
- "jstests/ajax_url2.html",
- "jstests/ajaxresult.json",
- ),
- ),
- )
-
-
-if __name__ == '__main__':
- unittest_main()
--- a/web/test/test_windmill.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-# Run all scenarii found in windmill directory
-from os.path import join, dirname
-from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
- unittest_main)
-
-class CubicWebWindmillUseCase(CubicWebWindmillUseCase):
- #test_dir = join(dirname(__file__), "windmill/test_edit_relation.py")
- pass
-
-
-if __name__ == '__main__':
- unittest_main()
--- a/web/test/unittest_application.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_application.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/web/test/unittest_facet.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_facet.py Wed Jul 20 18:21:47 2011 +0200
@@ -66,18 +66,18 @@
self.assertEqual(f.vocabulary(),
[(u'guests', guests), (u'managers', managers)])
# ensure rqlst is left unmodified
- self.assertEqual(rqlst.as_string(), 'DISTINCT Any GROUPBY X WHERE X in_group G?, G name GN, NOT G name "users"')
+ self.assertEqual(rqlst.as_string(), 'DISTINCT Any WHERE X in_group G?, G name GN, NOT G name "users"')
#rqlst = rset.syntax_tree()
self.assertEqual(sorted(f.possible_values()),
[str(guests), str(managers)])
# ensure rqlst is left unmodified
- self.assertEqual(rqlst.as_string(), 'DISTINCT Any GROUPBY X WHERE X in_group G?, G name GN, NOT G name "users"')
+ self.assertEqual(rqlst.as_string(), 'DISTINCT Any WHERE X in_group G?, G name GN, NOT G name "users"')
req.form[f.__regid__] = str(guests)
f.add_rql_restrictions()
# selection is cluttered because rqlst has been prepared for facet (it
# is not in real life)
self.assertEqual(f.rqlst.as_string(),
- 'DISTINCT Any GROUPBY X WHERE X in_group G?, G name GN, NOT G name "users", X in_group D, D eid %s' % guests)
+ 'DISTINCT Any WHERE X in_group G?, G name GN, NOT G name "users", X in_group D, D eid %s' % guests)
def test_relation_no_relation_1(self):
f, (guests, managers) = self._in_group_facet(no_relation=True)
--- a/web/test/unittest_form.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_form.py Wed Jul 20 18:21:47 2011 +0200
@@ -104,9 +104,9 @@
def test_reledit_composite_field(self):
rset = self.execute('INSERT BlogEntry X: X title "cubicweb.org", X content "hop"')
- form = self.vreg['views'].select('doreledit', self.request(),
+ form = self.vreg['views'].select('reledit', self.request(),
rset=rset, row=0, rtype='content')
- data = form.render(row=0, rtype='content', formid='base')
+ data = form.render(row=0, rtype='content', formid='base', action='edit_rtype')
self.failUnless('content_format' in data)
# form view tests #########################################################
--- a/web/test/unittest_reledit.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_reledit.py Wed Jul 20 18:21:47 2011 +0200
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""
-mainly regression-preventing tests for reledit/doreledit views
+mainly regression-preventing tests for reledit views
"""
from cubicweb.devtools.testlib import CubicWebTC
@@ -33,9 +33,9 @@
class ClickAndEditFormTC(ReleditMixinTC, CubicWebTC):
def test_default_config(self):
- reledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited');" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
- 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ reledit = {'title': '''<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '', 'edit_rtype');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>''',
+ 'long_desc': '''<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited', 'add');" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>''',
+ 'manager': '''<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited', 'edit_rtype');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>''',
'composite_card11_2ttypes': """<not specified>""",
'concerns': """<not specified>"""}
@@ -44,9 +44,11 @@
continue
rtype = rschema.type
self.assertMultiLineEqual(reledit[rtype] % {'eid': self.proj.eid},
- self.proj.view('reledit', rtype=rtype, role=role), rtype)
+ self.proj.view('reledit', rtype=rtype, role=role),
+ rtype)
def test_default_forms(self):
+ self.skipTest('Need to check if this test should still run post reledit/doreledit merge')
doreledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="title-subject-%(eid)s-form" onsubmit="return freezeFormButtons('title-subject-%(eid)s-form');" class="releditForm" cubicweb:target="eformframe">
<fieldset>
<input name="__form_id" type="hidden" value="base" />
@@ -190,11 +192,11 @@
reledit_ctrl.tag_object_of(('Ticket', 'concerns', 'Project'),
{'edit_target': 'rtype'})
reledit = {
- 'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', true, '');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><long_desc is required></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', true, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm('deleteconf', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to delete this value"><img title="click to delete this value" src="http://testing.fr/cubicweb/data/cancel.png" alt="click to delete this value"/></div></div></div>""",
+ 'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', true, '', 'edit_rtype');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><long_desc is required></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', true, 'autolimited', 'edit_rtype');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited', 'edit_related');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm('deleteconf', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited', 'delete');" title="click to delete this value"><img title="click to delete this value" src="http://testing.fr/cubicweb/data/cancel.png" alt="click to delete this value"/></div></div></div>""",
'composite_card11_2ttypes': """<not specified>""",
- 'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'concerns', 'object', 'concerns-object-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
+ 'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'concerns', 'object', 'concerns-object-%(eid)s', false, 'autolimited', 'edit_rtype');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
}
for rschema, ttypes, role in self.proj.e_schema.relation_definitions(includefinal=True):
if rschema not in reledit:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_request.py Wed Jul 20 18:21:47 2011 +0200
@@ -0,0 +1,69 @@
+"""misc. unittests for utility functions
+"""
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from functools import partial
+
+from cubicweb.web.request import (_parse_accept_header,
+ _mimetype_sort_key, _mimetype_parser, _charset_sort_key)
+
+
+
+class AcceptParserTC(TestCase):
+
+ def test_parse_accept(self):
+ parse_accept_header = partial(_parse_accept_header,
+ value_parser=_mimetype_parser,
+ value_sort_key=_mimetype_sort_key)
+ # compare scores
+ self.assertEqual(parse_accept_header("audio/*;q=0.2, audio/basic"),
+ [( ('audio/basic', ('audio', 'basic', {}), 1.0 ) ),
+ ( ('audio/*', ('audio', '*', {}), 0.2 ) )])
+ self.assertEqual(parse_accept_header("text/plain;q=0.5, text/html, text/x-dvi;q=0.8, text/x-c"),
+ [( ('text/html', ('text', 'html', {}), 1.0 ) ),
+ ( ('text/x-c', ('text', 'x-c', {}), 1.0 ) ),
+ ( ('text/x-dvi', ('text', 'x-dvi', {}), 0.8 ) ),
+ ( ('text/plain', ('text', 'plain', {}), 0.5 ) )])
+ # compare mimetype precedence for a same given score
+ self.assertEqual(parse_accept_header("audio/*, audio/basic"),
+ [( ('audio/basic', ('audio', 'basic', {}), 1.0 ) ),
+ ( ('audio/*', ('audio', '*', {}), 1.0 ) )])
+ self.assertEqual(parse_accept_header("text/*, text/html, text/html;level=1, */*"),
+ [( ('text/html', ('text', 'html', {'level': '1'}), 1.0 ) ),
+ ( ('text/html', ('text', 'html', {}), 1.0 ) ),
+ ( ('text/*', ('text', '*', {}), 1.0 ) ),
+ ( ('*/*', ('*', '*', {}), 1.0 ) )])
+ # free party
+ self.assertEqual(parse_accept_header("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"),
+ [( ('text/html', ('text', 'html', {'level': '1'}), 1.0 ) ),
+ ( ('text/html', ('text', 'html', {}), 0.7 ) ),
+ ( ('*/*', ('*', '*', {}), 0.5 ) ),
+ ( ('text/html', ('text', 'html', {'level': '2'}), 0.4 ) ),
+ ( ('text/*', ('text', '*', {}), 0.3 ) )
+ ])
+ # chrome sample header
+ self.assertEqual(parse_accept_header("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"),
+ [( ('application/xhtml+xml', ('application', 'xhtml+xml', {}), 1.0 ) ),
+ ( ('application/xml', ('application', 'xml', {}), 1.0 ) ),
+ ( ('image/png', ('image', 'png', {}), 1.0 ) ),
+ ( ('text/html', ('text', 'html', {}), 0.9 ) ),
+ ( ('text/plain', ('text', 'plain', {}), 0.8 ) ),
+ ( ('*/*', ('*', '*', {}), 0.5 ) ),
+ ])
+
+ def test_parse_accept_language(self):
+ self.assertEqual(_parse_accept_header('fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3'),
+ [('fr', 'fr', 1.0), ('fr-fr', 'fr-fr', 0.8),
+ ('en-us', 'en-us', 0.5), ('en', 'en', 0.3)])
+
+ def test_parse_accept_charset(self):
+ parse_accept_header = partial(_parse_accept_header,
+ value_sort_key=_charset_sort_key)
+ self.assertEqual(parse_accept_header('ISO-8859-1,utf-8;q=0.7,*;q=0.7'),
+ [('ISO-8859-1', 'ISO-8859-1', 1.0),
+ ('utf-8', 'utf-8', 0.7),
+ ('*', '*', 0.7)])
+
+if __name__ == '__main__':
+ unittest_main()
--- a/web/test/unittest_urlpublisher.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_urlpublisher.py Wed Jul 20 18:21:47 2011 +0200
@@ -33,8 +33,8 @@
"""test suite for QSPreProcessor"""
def setup_database(self):
- self.create_user(u'ÿsaÿe')
req = self.request()
+ self.create_user(req, u'ÿsaÿe')
b = req.create_entity('BlogEntry', title=u'hell\'o', content=u'blabla')
c = req.create_entity('Tag', name=u'yo') # take care: Tag's name normalized to lower case
self.execute('SET C tags B WHERE C eid %(c)s, B eid %(b)s', {'c':c.eid, 'b':b.eid})
--- a/web/test/unittest_urlrewrite.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_urlrewrite.py Wed Jul 20 18:21:47 2011 +0200
@@ -103,9 +103,10 @@
class RgxActionRewriteTC(CubicWebTC):
def setup_database(self):
- self.p1 = self.create_user(u'user1')
+ req = self.request()
+ self.p1 = self.create_user(req, u'user1')
self.p1.set_attributes(firstname=u'joe', surname=u'Dalton')
- self.p2 = self.create_user(u'user2')
+ self.p2 = self.create_user(req, u'user2')
self.p2.set_attributes(firstname=u'jack', surname=u'Dalton')
def test_rgx_action_with_transforms(self):
--- a/web/test/unittest_views_basecontrollers.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_views_basecontrollers.py Wed Jul 20 18:21:47 2011 +0200
@@ -93,9 +93,9 @@
self.assertEqual([g.eid for g in e.in_group], groupeids)
def test_user_can_change_its_password(self):
- user = self.create_user('user')
+ req = self.request()
+ user = self.create_user(req, 'user')
cnx = self.login('user')
- req = self.request()
eid = u(user.eid)
req.form = {
'eid': eid, '__maineid' : eid,
@@ -160,8 +160,8 @@
self.assertEqual(email.address, 'dima@logilab.fr')
def test_edit_multiple_linked(self):
- peid = u(self.create_user('adim').eid)
req = self.request()
+ peid = u(self.create_user(req, 'adim').eid)
req.form = {'eid': [peid, 'Y'], '__maineid': peid,
'__type:'+peid: u'CWUser',
@@ -450,7 +450,8 @@
def test_nonregr_rollback_on_validation_error(self):
- p = self.create_user("doe")
+ req = self.request()
+ p = self.create_user(req, "doe")
# do not try to skip 'primary_email' for this test
old_skips = p.__class__.skip_copy_for
p.__class__.skip_copy_for = ()
@@ -497,7 +498,7 @@
class ReportBugControllerTC(CubicWebTC):
- def test_usable_by_guets(self):
+ def test_usable_by_guest(self):
self.login('anon')
self.assertRaises(NoSelectableObject,
self.vreg['controllers'].select, 'reportbug', self.request())
@@ -506,7 +507,7 @@
class SendMailControllerTC(CubicWebTC):
- def test_not_usable_by_guets(self):
+ def test_not_usable_by_guest(self):
self.assertRaises(NoSelectableObject,
self.vreg['controllers'].select, 'sendmail', self.request())
self.vreg['controllers'].select('sendmail',
@@ -529,7 +530,7 @@
req = self.request()
self.pytag = req.create_entity('Tag', name=u'python')
self.cubicwebtag = req.create_entity('Tag', name=u'cubicweb')
- self.john = self.create_user(u'John')
+ self.john = self.create_user(req, u'John')
## tests ##################################################################
--- a/web/test/unittest_views_baseviews.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_views_baseviews.py Wed Jul 20 18:21:47 2011 +0200
@@ -16,11 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
+
from logilab.common.testlib import unittest_main
from logilab.mtconverter import html_unescape
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.utils import json
+from cubicweb.view import StartupView, TRANSITIONAL_DOCTYPE_NOEXT
from cubicweb.web.htmlwidgets import TableWidget
from cubicweb.web.views import vid_from_rset
@@ -125,5 +128,47 @@
self.assertListEqual(got, expected)
+class HTMLStreamTests(CubicWebTC):
+
+ def test_set_doctype_reset_xmldecl(self):
+ """
+ tests `cubicweb.web.request.CubicWebRequestBase.set_doctype`
+ with xmldecl reset
+ """
+ class MyView(StartupView):
+ __regid__ = 'my-view'
+ def call(self):
+ self._cw.set_doctype('<!DOCTYPE html>')
+
+ with self.temporary_appobjects(MyView):
+ html_source = self.view('my-view').source
+ source_lines = [line.strip() for line in html_source.splitlines(False)
+ if line.strip()]
+ self.assertListEqual(source_lines[:2],
+ ['<!DOCTYPE html>',
+ '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" xml:lang="en" lang="en">'])
+
+ def test_set_doctype_no_reset_xmldecl(self):
+ """
+ tests `cubicweb.web.request.CubicWebRequestBase.set_doctype`
+ with no xmldecl reset
+ """
+ html_doctype = TRANSITIONAL_DOCTYPE_NOEXT.strip()
+ class MyView(StartupView):
+ __regid__ = 'my-view'
+ def call(self):
+ self._cw.set_doctype(html_doctype, reset_xmldecl=False)
+ self._cw.main_stream.set_namespaces([('xmlns', 'http://www.w3.org/1999/xhtml')])
+ self._cw.main_stream.set_htmlattrs([('lang', 'cz')])
+
+ with self.temporary_appobjects(MyView):
+ html_source = self.view('my-view').source
+ source_lines = [line.strip() for line in html_source.splitlines(False)
+ if line.strip()]
+ self.assertListEqual(source_lines[:3],
+ ['<?xml version="1.0" encoding="UTF-8"?>',
+ html_doctype,
+ '<html xmlns="http://www.w3.org/1999/xhtml" lang="cz">'])
+
if __name__ == '__main__':
unittest_main()
--- a/web/test/unittest_viewselector.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_viewselector.py Wed Jul 20 18:21:47 2011 +0200
@@ -180,7 +180,7 @@
self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req1, rset=rset2)
def test_propertiesform_jdoe(self):
- self.create_user('jdoe')
+ self.create_user(self.request(), 'jdoe')
self.login('jdoe')
req1 = self.request()
req2 = self.request()
--- a/web/test/unittest_web.py Mon May 16 16:24:00 2011 +0200
+++ b/web/test/unittest_web.py Wed Jul 20 18:21:47 2011 +0200
@@ -38,7 +38,7 @@
self.failUnless(url.endswith('()'))
cbname = url.split()[1][:-2]
self.assertMultiLineEqual(
- 'function %s() { $("#foo").loadxhtml("http://testing.fr/cubicweb/json?%s",null,"get","replace"); }' % (cbname, qs),
+ 'function %s() { $("#foo").loadxhtml("http://testing.fr/cubicweb/json?%s",{"pageid": "%s"},"get","replace"); }' % (cbname, qs, req.pageid),
req.html_headers.post_inlined_scripts[0])
if __name__ == '__main__':
--- a/web/test/windmill/test_connexion.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-from cubicweb.devtools import DEFAULT_SOURCES
-LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
-
-# Generated by the windmill services transformer
-from windmill.authoring import WindmillTestClient
-
-def test_connect():
- client = WindmillTestClient(__name__)
-
- client.open(url=u'/')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
- client.type(text=LOGIN, id=u'__login')
- client.type(text=PASSWORD, id=u'__password')
-
- client.execJS(js=u"$('#loginForm').submit()")
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.sleep(milliseconds=u'5000')
- client.asserts.assertJS(js=u'$(\'.message\').text() == "welcome %s !"' % LOGIN)
- client.open(url=u'/logout')
- client.waits.forPageLoad(timeout=u'20000')
- client.open(url=u'/')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
-
-
-def test_wrong_connect():
- client = WindmillTestClient(__name__)
-
- client.open(url=u'/')
- # XXX windmill wants to use its proxy internally on 403 :-(
- #client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
- #client.type(text=LOGIN, id=u'__login')
- #client.type(text=u'novalidpassword', id=u'__password')
- #client.click(value=u'log in')
- client.open(url=u'/?__login=user&__password=nopassword')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertTextIn(validator=u'authentication failure', id=u'loginBox')
- client.open(url=u'/')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
--- a/web/test/windmill/test_creation.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-from cubicweb.devtools import DEFAULT_SOURCES
-LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
-
-# Generated by the windmill services transformer
-from windmill.authoring import WindmillTestClient
-
-def test_creation():
- client = WindmillTestClient(__name__)
-
- client.open(url=u'/')
- client.waits.forPageLoad(timeout=u'8000')
- client.type(text=LOGIN, id=u'__login')
- client.type(text=PASSWORD, id=u'__password')
- client.click(value=u'log in')
- client.waits.forPageLoad(timeout=u'20000')
-
- # pre-condition
- client.open(url=u'/cwuser/myuser')
- client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "this resource does not exist"')
- client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
- client.asserts.assertJS(js=u'$(\'.searchMessage strong\').text() == "No result matching query"')
-
- client.open(url=u'/manage')
- client.open(url=u'/add/CWUser')
- client.type(text=u'myuser', id=u'login-subject:A')
- client.type(text=u'myuser', id=u'upassword-subject:A')
- client.type(text=u'myuser', name=u'upassword-subject-confirm:A')
- client.type(text=u'myuser', id=u'firstname-subject:A')
- client.select(option=u'managers', id=u'from_in_group-subject:A')
- client.click(id=u'cwinoutadd')
- client.waits.forPageLoad(timeout=u'20000')
- client.click(id=u'adduse_email:Alink')
- client.waits.forPageLoad(timeout=u'20000')
- client.type(text=u'myuser@logilab.fr', id=u'address-subject:B')
- client.waits.forPageLoad(timeout=u'20000')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.sleep(milliseconds=u'5000')
- client.asserts.assertJS(js=u'$(\'.message\').text() == "entity created"')
- client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "myuser"')
- client.waits.forPageLoad(timeout=u'8000')
- client.open(url=u'/cwuser/myuser?vid=sameetypelist')
- client.waits.forPageLoad(timeout=u'8000')
- client.asserts.assertJS(js=u'$(\'#contentmain a\').text() == "myuser"')
- client.open(url=u'/cwuser/myuser?vid=text')
- client.waits.forPageLoad(timeout=u'8000')
- client.asserts.assertJS(js=u'$(\'#contentmain\').text() == "\\nmyuser"')
- client.open(url=u'/cwuser/myuser?vid=deleteconf')
- client.waits.forElement(timeout=u'8000', value=u'button_delete')
- client.click(value=u'button_delete')
- client.waits.forPageLoad(timeout=u'8000')
- client.open(url=u'/cwuser/myuser')
- client.asserts.assertJS(js=u'$(\'#contentmain h1\').text() == "this resource does not exist"')
- client.open(url=u'/?rql=Any U WHERE U is CWUser, U login "myuser"')
- client.asserts.assertJS(js=u'$(\'.searchMessage strong\').text() == "No result matching query"')
-
--- a/web/test/windmill/test_edit_relation.py Mon May 16 16:24:00 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-from cubicweb.devtools import DEFAULT_SOURCES
-LOGIN, PASSWORD = DEFAULT_SOURCES['admin'].values()
-
-# Generated by the windmill services transformer
-from windmill.authoring import WindmillTestClient
-
-
-def test_edit_relation():
- client = WindmillTestClient(__name__)
-
- client.open(url=u'/logout')
- client.open(url=u'/')
- client.asserts.assertJS(js=u"$('#loginForm').is(':visible')")
- client.type(text=LOGIN, id=u'__login')
- client.type(text=PASSWORD, id=u'__password')
- client.execJS(js=u"$('#loginForm').submit()")
- client.waits.forPageLoad(timeout=u'20000')
- client.open(url=u'/add/Folder')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(timeout=u'8000', id=u'name-subject:A')
- client.click(id=u'name-subject:A')
- client.type(text=u'folder1', id=u'name-subject:A')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'add add Folder filed_under Folder object', timeout=u'8000')
- client.click(link=u'add add Folder filed_under Folder object')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(timeout=u'8000', id=u'name-subject:A')
- client.click(id=u'name-subject:A')
- client.type(text=u'subfolder1', id=u'name-subject:A')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'more actions', timeout=u'8000')
- client.click(link=u'more actions')
- client.click(link=u'copy')
- client.waits.forPageLoad(timeout=u'20000')
- client.type(text=u'folder2', id=u'name-subject:A')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'modify', timeout=u'8000')
- client.click(link=u'modify')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(timeout=u'8000', id=u'footer')
- client.click(link=u'x')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'add add Folder filed_under Folder object', timeout=u'8000')
- client.click(link=u'add add Folder filed_under Folder object')
- client.waits.forPageLoad(timeout=u'20000')
- client.type(text=u'subfolder2', id=u'name-subject:A')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'subfolder2', timeout=u'8000')
- client.click(link=u'subfolder2')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(link=u'modify', timeout=u'8000')
- client.click(link=u'modify')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(timeout=u'8000', id=u'footer')
- client.click(link=u'x')
- client.select(xpath=u'//select', index=u'1')
- #client.execJQuery(jquery=u'("select").trigger(\'change\')') # BUGGY freeze UI..
- client.execJS(js=u'$("select").trigger(\'change\')')
- client.waits.sleep(milliseconds=u'2000')
- client.select(jquery=u'(\'select:contains("Search")\')[0]', option=u'Search for folder')
- client.waits.forPageLoad(timeout=u'20000')
- client.click(link=u'folder1')
- client.waits.forPageLoad(timeout=u'20000')
- client.waits.forElement(timeout=u'8000', value=u'button_ok')
- client.click(value=u'button_ok')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertText(xpath=u'//h1', validator=u'subfolder2')
- client.waits.forElement(link=u'folder_plural', timeout=u'8000')
- client.click(link=u'folder_plural')
- client.waits.forPageLoad(timeout=u'20000')
- client.asserts.assertText(jquery=u"('#contentmain div a')[0]", validator=u'folder1')
- client.asserts.assertText(jquery=u"('#contentmain div a')[1]", validator=u'folder2')
- client.asserts.assertText(jquery=u"('#contentmain div a')[2]", validator=u'subfolder1')
- client.asserts.assertText(jquery=u"('#contentmain div a')[3]", validator=u'subfolder2')
- client.click(link=u'subfolder2')
- client.click(link=u'modify')
- client.click(link=u'folder1')
--- a/web/views/autoform.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/autoform.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/web/views/basecomponents.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/basecomponents.py Wed Jul 20 18:21:47 2011 +0200
@@ -29,8 +29,8 @@
from logilab.common.deprecation import class_renamed
from rql import parse
-from cubicweb.selectors import (yes, multi_etypes_rset, match_form_params,
- match_context, configuration_values,
+from cubicweb.selectors import (yes, match_form_params, match_context,
+ multi_etypes_rset, configuration_values,
anonymous_user, authenticated_user)
from cubicweb.schema import display_name
from cubicweb.utils import wrap_on_write
@@ -88,6 +88,7 @@
class ApplLogo(HeaderComponent):
"""build the instance logo, usually displayed in the header"""
__regid__ = 'logo'
+ __select__ = yes() # no need for a cnx
order = -1
def render(self, w):
@@ -150,7 +151,7 @@
class AnonUserStatusLink(HeaderComponent):
__regid__ = 'userstatus'
- __select__ = HeaderComponent.__select__ & anonymous_user()
+ __select__ = anonymous_user()
context = _('header-right')
order = HeaderComponent.order - 10
@@ -159,7 +160,7 @@
class AuthenticatedUserStatus(AnonUserStatusLink):
- __select__ = HeaderComponent.__select__ & authenticated_user()
+ __select__ = authenticated_user()
def render(self, w):
# display useractions and siteactions
@@ -185,9 +186,18 @@
# don't want user to hide this component using an cwproperty
cw_property_defs = {}
- def call(self):
- msgs = [msg for msg in (self._cw.get_shared_data('sources_error', pop=True),
- self._cw.message) if msg]
+ def call(self, msg=None):
+ if msg is None:
+ msgs = []
+ if self._cw.cnx:
+ srcmsg = self._cw.get_shared_data('sources_error', pop=True)
+ if srcmsg:
+ msgs.append(srcmsg)
+ reqmsg = self._cw.message # XXX don't call self._cw.message twice
+ if reqmsg:
+ msgs.append(reqmsg)
+ else:
+ msgs = [msg]
self.w(u'<div id="appMsg" onclick="%s" class="%s">\n' %
(toggle_action('appMsg'), (msgs and ' ' or 'hidden')))
for msg in msgs:
--- a/web/views/basecontrollers.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/basecontrollers.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -281,7 +281,7 @@
try:
args = [json.loads(arg) for arg in args]
except ValueError, exc:
- self.exception('error while decoding json arguments for js_%s: %s',
+ self.exception('error while decoding json arguments for js_%s: %s (err: %s)',
fname, args, exc)
raise RemoteCallFailed(exc_message(exc, self._cw.encoding))
try:
@@ -455,13 +455,13 @@
def js_reledit_form(self):
req = self._cw
args = dict((x, req.form[x])
- for x in ('formid', 'rtype', 'role', 'reload'))
+ for x in ('formid', 'rtype', 'role', 'reload', 'action'))
rset = req.eid_rset(typed_eid(self._cw.form['eid']))
try:
args['reload'] = json.loads(args['reload'])
except ValueError: # not true/false, an absolute url
assert args['reload'].startswith('http')
- view = req.vreg['views'].select('doreledit', req, rset=rset, rtype=args['rtype'])
+ view = req.vreg['views'].select('reledit', req, rset=rset, rtype=args['rtype'])
return self._call_view(view, **args)
@jsonize
@@ -603,16 +603,14 @@
errors = self._cw.cnx.undo_transaction(txuuid)
if not errors:
self.redirect()
- return self._cw._('some errors occurred:') + self.view('pyvalist',
- pyvalue=errors)
+ raise ValidationError(None, {None: '\n'.join(errors)})
- def redirect(self):
+ def redirect(self, msg=None):
req = self._cw
+ msg = msg or req._("transaction undone")
breadcrumbs = req.session.data.get('breadcrumbs', None)
if breadcrumbs is not None and len(breadcrumbs) > 1:
- url = req.rebuild_url(breadcrumbs[-2],
- __message=req._('transaction undoed'))
+ url = req.rebuild_url(breadcrumbs[-2], __message=msg)
else:
- url = req.build_url(__message=req._('transaction undoed'))
+ url = req.build_url(__message=msg)
raise Redirect(url)
-
--- a/web/views/basetemplates.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/basetemplates.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/web/views/baseviews.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/baseviews.py Wed Jul 20 18:21:47 2011 +0200
@@ -201,8 +201,15 @@
self.w(u'<span class="value">%s</span>'
% self._cw.format_date(entity.creation_date))
if entity.creator:
- self.w(u' <span>%s</span> ' % _('by'))
+ if entity.creation_date:
+ self.w(u' <span>%s</span> ' % _('by'))
+ else:
+ self.w(u' <span>%s</span> ' % _('created_by'))
self.w(u'<span class="value">%s</span>' % entity.creator.name())
+ meta = entity.cw_metainformation()
+ if meta['source']['uri'] != 'system':
+ self.w(u' (<span>%s</span>' % _('cw_source'))
+ self.w(u' <span class="value">%s</span>)' % meta['source']['uri'])
self.w(u'</div>')
--- a/web/views/boxes.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/boxes.py Wed Jul 20 18:21:47 2011 +0200
@@ -185,7 +185,7 @@
def render_body(self, w):
for category, views in box.sort_by_category(self.views):
- menu = htmlwidgets.BoxMenu(category)
+ menu = htmlwidgets.BoxMenu(self._cw._(category))
for view in views:
menu.append(self.action_link(view))
self.append(menu)
@@ -208,6 +208,7 @@
raise component.EmptyComponent()
self.items = []
+
class RsetBox(component.CtxComponent):
"""helper view class to display an rset in a sidebox"""
__select__ = nonempty_rset() & match_kwargs('title', 'vid')
--- a/web/views/cwsources.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/cwsources.py Wed Jul 20 18:21:47 2011 +0200
@@ -27,7 +27,7 @@
from cubicweb.selectors import is_instance, score_entity, match_user_groups
from cubicweb.view import EntityView, StartupView
from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, display_name
-from cubicweb.web import uicfg
+from cubicweb.web import uicfg, formwidgets as wdgs
from cubicweb.web.views import tabs, actions
@@ -35,6 +35,12 @@
_abaa.tag_object_of(('CWSourceSchemaConfig', 'cw_schema', '*'), False)
_abaa.tag_object_of(('CWSourceSchemaConfig', 'cw_for_source', '*'), False)
+_afs = uicfg.autoform_section
+_afs.tag_attribute(('CWSource', 'synchronizing'), 'main', 'hidden')
+_afs.tag_object_of(('*', 'cw_for_source', 'CWSource'), 'main', 'hidden')
+_affk = uicfg.autoform_field_kwargs
+_affk.tag_attribute(('CWSource', 'parser'), {'widget': wdgs.TextInput})
+
# source primary views #########################################################
_pvs = uicfg.primaryview_section
@@ -126,6 +132,8 @@
else: # CWAttribute/CWRelation
self.srelations.setdefault(cwerschema.rtype.name, []).append(
(cwerschema.stype.name, cwerschema.otype.name) )
+ self.sentities.add(cwerschema.stype.name)
+ self.sentities.add(cwerschema.otype.name)
def check(self):
self.init()
@@ -154,14 +162,15 @@
warning(_('relation %(rtype)s with %(etype)s as %(role)s is '
'supported but no target type supported') %
{'rtype': rschema, 'role': role, 'etype': etype})
- for rtype in self.srelations:
- rschema = self.schema[rtype]
- for subj, obj in rschema.rdefs:
- if subj in self.sentities and obj in self.sentities:
- break
- else:
- error(_('relation %s is supported but none if its definitions '
- 'matches supported entities') % rtype)
+ for rtype, rdefs in self.srelations.iteritems():
+ if rdefs is None:
+ rschema = self.schema[rtype]
+ for subj, obj in rschema.rdefs:
+ if subj in self.sentities and obj in self.sentities:
+ break
+ else:
+ error(_('relation %s is supported but none of its definitions '
+ 'matches supported entities') % rtype)
self.custom_check()
def custom_check(self):
--- a/web/views/cwuser.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/cwuser.py Wed Jul 20 18:21:47 2011 +0200
@@ -24,6 +24,7 @@
from logilab.mtconverter import xml_escape
+from cubicweb import tags
from cubicweb.schema import display_name
from cubicweb.selectors import one_line_rset, is_instance, match_user_groups
from cubicweb.view import EntityView, StartupView
@@ -32,16 +33,8 @@
_pvs = uicfg.primaryview_section
_pvs.tag_attribute(('CWUser', 'login'), 'hidden')
-_pvs.tag_attribute(('CWGroup', 'name'), 'hidden')
-_pvs.tag_subject_of(('CWGroup', 'read_permission', '*'), 'relations')
-_pvs.tag_subject_of(('CWGroup', 'add_permission', '*'), 'relations')
-_pvs.tag_subject_of(('CWGroup', 'delete_permission', '*'), 'relations')
-_pvs.tag_subject_of(('CWGroup', 'update_permission', '*'), 'relations')
-_pvs.tag_object_of(('*', 'in_group', 'CWGroup'), 'relations')
-_pvs.tag_object_of(('*', 'require_group', 'CWGroup'), 'relations')
_affk = uicfg.autoform_field_kwargs
-
_affk.tag_subject_of(('CWUser', 'in_group', 'CWGroup'),
{'widget': formwidgets.InOutWidget})
@@ -99,6 +92,11 @@
# group views ##################################################################
+_pvs.tag_attribute(('CWGroup', 'name'), 'hidden')
+_pvs.tag_subject_of(('CWGroup', 'read_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'add_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'delete_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'update_permission', '*'), 'relations')
_pvs.tag_object_of(('CWUser', 'in_group', 'CWGroup'), 'hidden')
_pvs.tag_object_of(('*', 'require_group', 'CWGroup'), 'hidden')
@@ -170,7 +168,11 @@
class CWUserManagementView(StartupView):
__regid__ = 'cw.user-management'
- rql = ('Any U, F, S, U, L ORDERBY L WHERE U is CWUser, U login L, U firstname F, U surname S')
+ rql = ('Any U,USN,F,S,U,UAA,UDS, L,UAA,UDSN ORDERBY L WHERE U is CWUser, '
+ 'U login L, U firstname F, U surname S, '
+ 'U in_state US, US name USN, '
+ 'U primary_email UA?, UA address UAA, '
+ 'U cw_source UDS, US name UDSN')
title = _('users and groups management')
def call(self, **kwargs):
@@ -180,7 +182,7 @@
if eschema.has_perm(self._cw, 'add'):
self.w(u'<a href="%s" class="addButton right">%s</a>' % (
self._cw.build_url('add/%s' % eschema),
- self._cw._('add a %s' % etype).capitalize()))
+ self._cw.__('New %s' % etype).capitalize()))
self.w(u'<div class="clear"></div>')
self.wview('cw.user-table', self._cw.execute(self.rql))
@@ -191,10 +193,15 @@
def call(self, **kwargs):
headers = (display_name(self._cw, 'CWUser', 'plural'),
+ display_name(self._cw, 'in_state'),
self._cw._('firstname'), self._cw._('surname'),
- display_name(self._cw, 'CWGroup', 'plural'))
+ display_name(self._cw, 'CWGroup', 'plural'),
+ display_name(self._cw, 'primary_email'),
+ display_name(self._cw, 'CWSource'))
super(CWUserTable, self).call(
- paginate=True, cellvids={3: 'cw.user-table.group-cell'},
+ paginate=True, displayfilter=True,
+ cellvids={0: 'cw.user.login',
+ 4: 'cw.user-table.group-cell'},
headers=headers, **kwargs)
@@ -205,3 +212,11 @@
def cell_call(self, row, col, **kwargs):
entity = self.cw_rset.get_entity(row, col)
self.w(entity.view('reledit', rtype='in_group', role='subject'))
+
+class CWUserLoginCell(EntityView):
+ __regid__ = 'cw.user.login'
+ __select__ = is_instance('CWUser')
+
+ def cell_call(self, row, col, **kwargs):
+ entity = self.cw_rset.get_entity(row, col)
+ self.w(tags.a(entity.login, href=entity.absolute_url()))
--- a/web/views/debug.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/debug.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -133,6 +133,9 @@
if sessions:
w(u'<ul>')
for session in sessions:
+ if not session.cnx:
+ w(u'<li>%s (NO CNX)</li>' % session.sessionid)
+ continue
try:
last_usage_time = session.cnx.check()
except BadConnectionId:
--- a/web/views/formrenderers.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/formrenderers.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -40,7 +40,7 @@
from logilab.common import dictattr
from logilab.mtconverter import xml_escape
-from cubicweb import tags
+from cubicweb import tags, uilib
from cubicweb.appobject import AppObject
from cubicweb.selectors import is_instance, yes
from cubicweb.utils import json_dumps, support_args
@@ -112,18 +112,21 @@
data = []
_w = data.append
_w(self.open_form(form, values))
- if self.display_progress_div:
- _w(u'<div id="progress">%s</div>' % self._cw._('validating...'))
- _w(u'\n<fieldset>\n')
- self.render_fields(_w, form, values)
- self.render_buttons(_w, form)
- _w(u'\n</fieldset>\n')
+ self.render_content(_w, form, values)
_w(self.close_form(form, values))
errormsg = self.error_message(form)
if errormsg:
data.insert(0, errormsg)
w(''.join(data))
+ def render_content(self, w, form, values):
+ if self.display_progress_div:
+ w(u'<div id="progress">%s</div>' % self._cw._('validating...'))
+ w(u'\n<fieldset>\n')
+ self.render_fields(w, form, values)
+ self.render_buttons(w, form)
+ w(u'\n</fieldset>\n')
+
def render_label(self, form, field):
if field.label is None:
return u''
@@ -179,24 +182,25 @@
return u'<div class="errorMessage">%s</div>' % errormsg
return u''
- def open_form(self, form, values):
+ def open_form(self, form, values, **attrs):
if form.needs_multipart:
enctype = u'multipart/form-data'
else:
enctype = u'application/x-www-form-urlencoded'
- tag = (u'<form action="%s" method="post" enctype="%s"' % (
- xml_escape(form.form_action() or '#'), enctype))
+ attrs.setdefault('enctype', enctype)
+ attrs.setdefault('method', 'post')
+ attrs.setdefault('action', form.form_action() or '#')
if form.domid:
- tag += u' id="%s"' % form.domid
+ attrs.setdefault('id', form.domid)
if form.onsubmit:
- tag += u' onsubmit="%s"' % xml_escape(form.onsubmit % dictattr(form))
+ attrs.setdefault('onsubmit', form.onsubmit % dictattr(form))
if form.cssstyle:
- tag += u' style="%s"' % xml_escape(form.cssstyle)
+ attrs.setdefault('style', form.cssstyle)
if form.cssclass:
- tag += u' class="%s"' % xml_escape(form.cssclass)
+ attrs.setdefault('class', form.cssclass)
if form.cwtarget:
- tag += u' cubicweb:target="%s"' % xml_escape(form.cwtarget)
- return tag + u'>'
+ attrs.setdefault('cubicweb:target', form.cwtarget)
+ return '<form %s>' % uilib.sgml_attributes(attrs)
def close_form(self, form, values):
"""seems dumb but important for consistency w/ close form, and necessary
--- a/web/views/ibreadcrumbs.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/ibreadcrumbs.py Wed Jul 20 18:21:47 2011 +0200
@@ -195,7 +195,7 @@
def cell_call(self, row, col, **kwargs):
entity = self.cw_rset.get_entity(row, col)
- desc = xml_escape(uilib.cut(entity.dc_description(), 50))
+ desc = uilib.cut(entity.dc_description(), 50)
# NOTE remember camember: tags.a autoescapes
self.w(tags.a(entity.view('breadcrumbtext'),
href=entity.absolute_url(), title=desc))
--- a/web/views/idownloadable.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/idownloadable.py Wed Jul 20 18:21:47 2011 +0200
@@ -15,8 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""Specific views for entities adapting to IDownloadable"""
-
+"""
+Specific views for entities adapting to IDownloadable
+=====================================================
+"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -50,6 +52,7 @@
class DownloadBox(component.EntityCtxComponent):
+ """add download box"""
__regid__ = 'download_box' # no download box for images
__select__ = (component.EntityCtxComponent.__select__ &
adaptable('IDownloadable') & ~has_mimetype('image/'))
@@ -71,7 +74,9 @@
class DownloadView(EntityView):
- """this view is replacing the deprecated 'download' controller and allow
+ """download view
+
+ this view is replacing the deprecated 'download' controller and allow
downloading of entities providing the necessary interface
"""
__regid__ = 'download'
@@ -199,6 +204,7 @@
class ImageView(AbstractEmbeddedView):
+ """image embedded view"""
__regid__ = 'image'
__select__ = has_mimetype('image/')
@@ -207,6 +213,7 @@
class EHTMLView(AbstractEmbeddedView):
+ """html embedded view"""
__regid__ = 'ehtml'
__select__ = has_mimetype('text/html')
--- a/web/views/iprogress.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/iprogress.py Wed Jul 20 18:21:47 2011 +0200
@@ -177,12 +177,11 @@
@classmethod
def overrun(cls, iprogress):
- """overrun = done + todo - """
done = iprogress.done or 0
todo = iprogress.todo or 0
- revised_cost = iprogress.revised_cost or 0
- if done + todo > revised_cost:
- overrun = done + todo - revised_cost
+ budget = iprogress.revised_cost or 0
+ if done + todo > budget:
+ overrun = done + todo - budget
else:
overrun = 0
if overrun < cls.precision:
@@ -191,11 +190,10 @@
@classmethod
def overrun_percentage(cls, iprogress):
- """pourcentage overrun = overrun / budget"""
- revised_cost = iprogress.revised_cost or 0
- if revised_cost == 0:
+ budget = iprogress.revised_cost or 0
+ if budget == 0:
return 0
- return cls.overrun(iprogress) * 100. / revised_cost
+ return cls.overrun(iprogress) * 100. / budget
def cell_call(self, row, col):
self._cw.add_css('cubicweb.iprogress.css')
--- a/web/views/owl.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/owl.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -35,15 +35,19 @@
}
OWL_TYPE_MAP = {'String': 'xsd:string',
- 'Datetime': 'xsd:dateTime',
'Bytes': 'xsd:byte',
- 'Float': 'xsd:float',
+ 'Password': 'xsd:byte',
+
'Boolean': 'xsd:boolean',
'Int': 'xsd:int',
+ 'Float': 'xsd:float',
+ 'Decimal' : 'xsd:decimal',
+
'Date':'xsd:date',
+ 'Datetime': 'xsd:dateTime',
+ 'TZDatetime': 'xsd:dateTime',
'Time': 'xsd:time',
- 'Password': 'xsd:byte',
- 'Decimal' : 'xsd:decimal',
+ 'TZTime': 'xsd:time',
'Interval': 'xsd:duration'
}
--- a/web/views/plots.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/plots.py Wed Jul 20 18:21:47 2011 +0200
@@ -47,7 +47,7 @@
@objectify_selector
def columns_are_date_then_numbers(cls, req, rset=None, *args, **kwargs):
etypes = rset.description[0]
- if etypes[0] not in ('Date', 'Datetime'):
+ if etypes[0] not in ('Date', 'Datetime', 'TZDatetime'):
return 0
for etype in etypes[1:]:
if etype not in ('Int', 'Float'):
@@ -82,15 +82,17 @@
class FlotPlotWidget(PlotWidget):
"""PlotRenderer widget using Flot"""
onload = u"""
-var fig = jQuery("#%(figid)s");
+var fig = jQuery('#%(figid)s');
if (fig.attr('cubicweb:type') != 'prepared-plot') {
%(plotdefs)s
- jQuery.plot(jQuery("#%(figid)s"), [%(plotdata)s],
+ jQuery.plot(jQuery('#%(figid)s'), [%(plotdata)s],
{points: {show: true},
lines: {show: true},
grid: {hoverable: true},
+ /*yaxis : {tickFormatter : suffixFormatter},*/
xaxis: {mode: %(mode)s}});
- jQuery("#%(figid)s").bind("plothover", onPlotHover);
+ jQuery('#%(figid)s').data({mode: %(mode)s, dateformat: %(dateformat)s});
+ jQuery('#%(figid)s').bind('plothover', onPlotHover);
fig.attr('cubicweb:type','prepared-plot');
}
"""
@@ -101,11 +103,8 @@
self.timemode = timemode
def dump_plot(self, plot):
- # XXX for now, the only way that we have to customize properly
- # datetime labels on tooltips is to insert an additional column
- # cf. function onPlotHover in cubicweb.flot.js
if self.timemode:
- plot = [(datetime2ticks(x), y, datetime2ticks(x)) for x, y in plot]
+ plot = [(datetime2ticks(x), y) for x, y in plot]
return json_dumps(plot)
def _render(self, req, width=500, height=400):
@@ -122,11 +121,14 @@
plotdefs.append('var %s = %s;' % (plotid, self.dump_plot(plot)))
# XXX ugly but required in order to not crash my demo
plotdata.append("{label: '%s', data: %s}" % (label.replace(u'&', u''), plotid))
+ fmt = req.property_value('ui.date-format') # XXX datetime-format
+ # XXX TODO make plot options customizable
req.html_headers.add_onload(self.onload %
{'plotdefs': '\n'.join(plotdefs),
'figid': figid,
'plotdata': ','.join(plotdata),
- 'mode': self.timemode and "'time'" or 'null'})
+ 'mode': self.timemode and "'time'" or 'null',
+ 'dateformat': '"%s"' % fmt})
class PlotView(baseviews.AnyRsetView):
@@ -134,6 +136,7 @@
title = _('generic plot')
__select__ = multi_columns_rset() & all_columns_are_numbers()
timemode = False
+ paginable = False
def call(self, width=500, height=400):
# prepare data
@@ -181,6 +184,7 @@
class PieChartView(baseviews.AnyRsetView):
__regid__ = 'piechart'
pieclass = Pie
+ paginable = False
__select__ = multi_columns_rset() & second_column_is_number()
@@ -196,7 +200,7 @@
for rowidx, (_, value) in enumerate(self.cw_rset):
if value is not None:
vid = self._guess_vid(rowidx)
- label = '%s: %s' % (self.view(vid, self.cw_rset, row=rowidx, col=0),
+ label = '%s: %s' % (self._cw.view(vid, self.cw_rset, row=rowidx, col=0),
value)
labels.append(label.encode(self._cw.encoding))
values.append(value)
--- a/web/views/rdf.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/rdf.py Wed Jul 20 18:21:47 2011 +0200
@@ -59,6 +59,9 @@
self.entity2graph(graph, entity)
self.w(graph.serialize().decode('utf-8'))
+ def entity_call(self, entity):
+ self.call()
+
def entity2graph(self, graph, entity):
cwuri = URIRef(entity.cwuri)
add = graph.add
--- a/web/views/reledit.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/reledit.py Wed Jul 20 18:21:47 2011 +0200
@@ -15,7 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""the 'reledit' feature (eg edit attribute/relation from primary view)"""
+"""edit entity attributes/relations from any view, without going to the entity
+form
+"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -24,7 +26,8 @@
from warnings import warn
from logilab.mtconverter import xml_escape
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import deprecated, class_renamed
+from logilab.common.decorators import cached
from cubicweb import neg_role
from cubicweb.schema import display_name
@@ -43,16 +46,18 @@
return u''
def append_field(self, *args):
pass
+ def add_hidden(self, *args):
+ pass
rctrl = uicfg.reledit_ctrl
-class ClickAndEditFormView(EntityView):
- __regid__ = 'doreledit'
+class AutoClickAndEditFormView(EntityView):
+ __regid__ = 'reledit'
__select__ = non_final_entity() & match_kwargs('rtype')
# ui side continuations
_onclick = (u"cw.reledit.loadInlineEditionForm('%(formid)s', %(eid)s, '%(rtype)s', '%(role)s', "
- "'%(divid)s', %(reload)s, '%(vid)s');")
+ "'%(divid)s', %(reload)s, '%(vid)s', '%(action)s');")
_cancelclick = "cw.reledit.cleanupAfterCancel('%s')"
# ui side actions/buttons
@@ -75,93 +80,84 @@
# function taking the subject entity & returning a boolean or an eid
rvid=None, # vid to be applied to other side of rtype (non final relations only)
default_value=None,
- formid='base'
+ formid='base',
+ action=None
):
"""display field to edit entity's `rtype` relation on click"""
assert rtype
- assert role in ('subject', 'object'), '%s is not an acceptable role value' % role
self._cw.add_css('cubicweb.form.css')
self._cw.add_js(('cubicweb.reledit.js', 'cubicweb.edition.js', 'cubicweb.ajax.js'))
- entity = self.cw_rset.get_entity(row, col)
+ self.entity = self.cw_rset.get_entity(row, col)
rschema = self._cw.vreg.schema[rtype]
- self._rules = rctrl.etype_get(entity.e_schema.type, rschema.type, role, '*')
+ self._rules = rctrl.etype_get(self.entity.e_schema.type, rschema.type, role, '*')
if rvid is not None or default_value is not None:
warn('[3.9] specifying rvid/default_value on select is deprecated, '
'reledit_ctrl rtag to control this' % self, DeprecationWarning)
- reload = self._compute_reload(entity, rschema, role, reload)
- divid = self._build_divid(rtype, role, entity.eid)
+ reload = self._compute_reload(rschema, role, reload)
+ divid = self._build_divid(rtype, role, self.entity.eid)
if rschema.final:
- self._handle_attribute(entity, rschema, role, divid, reload)
+ self._handle_attribute(rschema, role, divid, reload, action)
else:
if self._is_composite():
- self._handle_composite(entity, rschema, role, divid, reload, formid)
+ self._handle_composite(rschema, role, divid, reload, formid, action)
else:
- self._handle_relation(entity, rschema, role, divid, reload, formid)
+ self._handle_relation(rschema, role, divid, reload, formid, action)
- def _handle_attribute(self, entity, rschema, role, divid, reload):
- rtype = rschema.type
- value = entity.printable_value(rtype)
- if not self._should_edit_attribute(entity, rschema):
+ def _handle_attribute(self, rschema, role, divid, reload, action):
+ value = self.entity.printable_value(rschema.type)
+ if not self._should_edit_attribute(rschema):
self.w(value)
return
- display_label, related_entity = self._prepare_form(entity, rtype, role)
- form, renderer = self._build_form(entity, rtype, role, divid, 'base',
- reload, display_label, related_entity)
+ form, renderer = self._build_form(self.entity, rschema, role, divid, 'base', reload, action)
value = value or self._compute_default_value(rschema, role)
self.view_form(divid, value, form, renderer)
- def _compute_formid_value(self, entity, rschema, role, rvid, formid):
- related_rset = entity.related(rschema.type, role)
+ def _compute_formid_value(self, rschema, role, rvid, formid):
+ related_rset = self.entity.related(rschema.type, role)
if related_rset:
value = self._cw.view(rvid, related_rset)
else:
value = self._compute_default_value(rschema, role)
- if not self._should_edit_relation(entity, rschema, role):
+ if not self._should_edit_relation(rschema, role):
return None, value
return formid, value
- def _handle_relation(self, entity, rschema, role, divid, reload, formid):
+ def _handle_relation(self, rschema, role, divid, reload, formid, action):
rvid = self._rules.get('rvid', 'autolimited')
- formid, value = self._compute_formid_value(entity, rschema, role, rvid, formid)
+ formid, value = self._compute_formid_value(rschema, role, rvid, formid)
if formid is None:
return self.w(value)
- rtype = rschema.type
- display_label, related_entity = self._prepare_form(entity, rtype, role)
- form, renderer = self._build_form(entity, rtype, role, divid, formid, reload,
- display_label, related_entity, dict(vid=rvid))
+ form, renderer = self._build_form(self.entity, rschema, role, divid, formid,
+ reload, action, dict(vid=rvid))
self.view_form(divid, value, form, renderer)
- def _handle_composite(self, entity, rschema, role, divid, reload, formid):
+ def _handle_composite(self, rschema, role, divid, reload, formid, action):
# this is for attribute-like composites (1 target type, 1 related entity at most, for now)
- ttypes = self._compute_ttypes(rschema, role)
+ entity = self.entity
related_rset = entity.related(rschema.type, role)
- add_related = self._may_add_related(related_rset, entity, rschema, role, ttypes)
- edit_related = self._may_edit_related_entity(related_rset, entity, rschema, role, ttypes)
- delete_related = edit_related and self._may_delete_related(related_rset, entity, rschema, role)
+ add_related = self._may_add_related(related_rset, rschema, role)
+ edit_related = self._may_edit_related_entity(related_rset, rschema, role)
+ delete_related = edit_related and self._may_delete_related(related_rset, rschema, role)
rvid = self._rules.get('rvid', 'autolimited')
- formid, value = self._compute_formid_value(entity, rschema, role, rvid, formid)
+ formid, value = self._compute_formid_value(rschema, role, rvid, formid)
if formid is None or not (edit_related or add_related):
# till we learn to handle cases where not (edit_related or add_related)
self.w(value)
return
- rtype = rschema.type
- ttype = ttypes[0]
- _fdata = self._prepare_composite_form(entity, rtype, role, edit_related,
- add_related and ttype)
- display_label, related_entity = _fdata
- form, renderer = self._build_form(entity, rtype, role, divid, formid, reload,
- display_label, related_entity, dict(vid=rvid))
+ form, renderer = self._build_form(entity, rschema, role, divid, formid,
+ reload, action, dict(vid=rvid))
self.view_form(divid, value, form, renderer,
edit_related, add_related, delete_related)
+ @cached
def _compute_ttypes(self, rschema, role):
dual_role = neg_role(role)
return getattr(rschema, '%ss' % dual_role)()
- def _compute_reload(self, entity, rschema, role, reload):
+ def _compute_reload(self, rschema, role, reload):
ctrl_reload = self._rules.get('reload', reload)
if callable(ctrl_reload):
- ctrl_reload = ctrl_reload(entity)
+ ctrl_reload = ctrl_reload(self.entity)
if isinstance(ctrl_reload, int) and ctrl_reload > 1: # not True/False
ctrl_reload = self._cw.build_url(ctrl_reload)
return ctrl_reload
@@ -179,33 +175,36 @@
def _is_composite(self):
return self._rules.get('edit_target') == 'related'
- def _may_add_related(self, related_rset, entity, rschema, role, ttypes):
+ def _may_add_related(self, related_rset, rschema, role):
""" ok for attribute-like composite entities """
+ ttypes = self._compute_ttypes(rschema, role)
if len(ttypes) > 1: # many etypes: learn how to do it
return False
- rdef = rschema.role_rdef(entity.e_schema, ttypes[0], role)
+ rdef = rschema.role_rdef(self.entity.e_schema, ttypes[0], role)
card = rdef.role_cardinality(role)
if related_rset or card not in '?1':
return False
if role == 'subject':
- kwargs = {'fromeid': entity.eid}
+ kwargs = {'fromeid': self.entity.eid}
else:
- kwargs = {'toeid': entity.eid}
+ kwargs = {'toeid': self.entity.eid}
return rdef.has_perm(self._cw, 'add', **kwargs)
- def _may_edit_related_entity(self, related_rset, entity, rschema, role, ttypes):
+ def _may_edit_related_entity(self, related_rset, rschema, role):
""" controls the edition of the related entity """
+ ttypes = self._compute_ttypes(rschema, role)
if len(ttypes) > 1 or len(related_rset.rows) != 1:
return False
- if entity.e_schema.rdef(rschema, role).role_cardinality(role) not in '?1':
+ if self.entity.e_schema.rdef(rschema, role).role_cardinality(role) not in '?1':
return False
return related_rset.get_entity(0, 0).cw_has_perm('update')
- def _may_delete_related(self, related_rset, entity, rschema, role):
+ def _may_delete_related(self, related_rset, rschema, role):
# we assume may_edit_related, only 1 related entity
if not related_rset:
return False
rentity = related_rset.get_entity(0, 0)
+ entity = self.entity
if role == 'subject':
kwargs = {'fromeid': entity.eid, 'toeid': rentity.eid}
else:
@@ -230,33 +229,33 @@
""" builds an id for the root div of a reledit widget """
return '%s-%s-%s' % (rtype, role, entity_eid)
- def _build_args(self, entity, rtype, role, formid, reload,
+ def _build_args(self, entity, rtype, role, formid, reload, action,
extradata=None):
divid = self._build_divid(rtype, role, entity.eid)
event_args = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'formid': formid,
- 'reload' : json_dumps(reload),
+ 'reload' : json_dumps(reload), 'action': action,
'role' : role, 'vid' : u''}
if extradata:
event_args.update(extradata)
return event_args
- def _prepare_form(self, entity, _rtype, role):
- display_label = False
- related_entity = entity
- return display_label, related_entity
-
- def _prepare_composite_form(self, entity, rtype, role, edit_related, add_related):
- display_label = True
- if edit_related and not add_related:
- related_entity = entity.related(rtype, role).get_entity(0, 0)
- elif add_related:
- _new_entity = self._cw.vreg['etypes'].etype_class(add_related)(self._cw)
+ def _prepare_form(self, entity, rschema, role, action):
+ assert action in ('edit_rtype', 'edit_related', 'add', 'delete'), action
+ if action == 'edit_rtype':
+ return False, entity
+ label = True
+ if action in ('edit_related', 'delete'):
+ edit_entity = entity.related(rschema, role).get_entity(0, 0)
+ elif action == 'add':
+ add_etype = self._compute_ttypes(rschema, role)[0]
+ _new_entity = self._cw.vreg['etypes'].etype_class(add_etype)(self._cw)
_new_entity.eid = self._cw.varmaker.next()
- related_entity = _new_entity
+ edit_entity = _new_entity
# XXX see forms.py ~ 276 and entities.linked_to method
# is there another way ?
- self._cw.form['__linkto'] = '%s:%s:%s' % (rtype, entity.eid, neg_role(role))
- return display_label, related_entity
+ self._cw.form['__linkto'] = '%s:%s:%s' % (rschema, entity.eid, neg_role(role))
+ assert edit_entity
+ return label, edit_entity
def _build_renderer(self, related_entity, display_label):
return self._cw.vreg['formrenderers'].select(
@@ -266,13 +265,18 @@
display_help=False, button_bar_class='buttonbar',
display_progress_div=False)
- def _build_form(self, entity, rtype, role, divid, formid, reload,
- display_label, related_entity, extradata=None, **formargs):
- event_args = self._build_args(entity, rtype, role, formid,
- reload, extradata)
+ def _build_form(self, entity, rschema, role, divid, formid, reload, action,
+ extradata=None, **formargs):
+ rtype = rschema.type
+ event_args = self._build_args(entity, rtype, role, formid, reload, action, extradata)
+ if not action:
+ form = _DummyForm()
+ form.event_args = event_args
+ return form, None
+ label, edit_entity = self._prepare_form(entity, rschema, role, action)
cancelclick = self._cancelclick % divid
form = self._cw.vreg['forms'].select(
- formid, self._cw, rset=related_entity.as_rset(), entity=related_entity,
+ formid, self._cw, rset=edit_entity.as_rset(), entity=edit_entity,
domid='%s-form' % divid, formtype='inlined',
action=self._cw.build_url('validateform', __onsuccess='window.parent.cw.reledit.onSuccess'),
cwtarget='eformframe', cssclass='releditForm',
@@ -298,9 +302,10 @@
if formid == 'base':
field = form.field_by_name(rtype, role, entity.e_schema)
form.append_field(field)
- return form, self._build_renderer(related_entity, display_label)
+ return form, self._build_renderer(edit_entity, label)
- def _should_edit_attribute(self, entity, rschema):
+ def _should_edit_attribute(self, rschema):
+ entity = self.entity
rdef = entity.e_schema.rdef(rschema)
# check permissions
if not entity.cw_has_perm('update'):
@@ -312,8 +317,8 @@
' use _should_edit_attribute instead',
_should_edit_attribute)
- def _should_edit_relation(self, entity, rschema, role):
- eeid = entity.eid
+ def _should_edit_relation(self, rschema, role):
+ eeid = self.entity.eid
perm_args = {'fromeid': eeid} if role == 'subject' else {'toeid': eeid}
return rschema.has_perm(self._cw, 'add', **perm_args)
@@ -335,9 +340,11 @@
w(u'<div id="%s" class="editableField hidden">' % divid)
def _edit_action(self, divid, args, edit_related, add_related, _delete_related):
+ # XXX disambiguate wrt edit_related
if not add_related: # currently, excludes edition
w = self.w
args['formid'] = 'edition' if edit_related else 'base'
+ args['action'] = 'edit_related' if edit_related else 'edit_rtype'
w(u'<div id="%s-update" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._editzonemsg)))
w(self._build_edit_zone())
@@ -346,7 +353,8 @@
def _add_action(self, divid, args, _edit_related, add_related, _delete_related):
if add_related:
w = self.w
- args['formid'] = 'edition' if add_related else 'base'
+ args['formid'] = 'edition'
+ args['action'] = 'add'
w(u'<div id="%s-add" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._addmsg)))
w(self._build_add_zone())
@@ -356,6 +364,7 @@
if delete_related:
w = self.w
args['formid'] = 'deleteconf'
+ args['action'] = 'delete'
w(u'<div id="%s-delete" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._deletemsg)))
w(self._build_delete_zone())
@@ -376,13 +385,4 @@
self._close_form_wrapper()
-class AutoClickAndEditFormView(ClickAndEditFormView):
- __regid__ = 'reledit'
-
- def _build_form(self, entity, rtype, role, divid, formid, reload,
- display_label, related_entity, extradata=None, **formargs):
- event_args = self._build_args(entity, rtype, role, 'base',
- reload, extradata)
- form = _DummyForm()
- form.event_args = event_args
- return form, None
+ClickAndEditFormView = class_renamed('ClickAndEditFormView', AutoClickAndEditFormView)
--- a/web/views/sessions.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/sessions.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -21,7 +21,7 @@
__docformat__ = "restructuredtext en"
-from cubicweb import RepositoryError, Unauthorized
+from cubicweb import RepositoryError, Unauthorized, AuthenticationError
from cubicweb.web import InvalidSession, Redirect
from cubicweb.web.application import AbstractSessionManager
from cubicweb.dbapi import DBAPISession
@@ -49,28 +49,36 @@
def get_session(self, req, sessionid):
"""return existing session for the given session identifier"""
- if not sessionid in self._sessions:
+ if sessionid not in self._sessions:
raise InvalidSession()
session = self._sessions[sessionid]
- try:
- user = self.authmanager.validate_session(req, session)
- except InvalidSession:
- # invalid session
- self.close_session(session)
- raise
- # associate the connection to the current request
- req.set_session(session, user)
+ if session.cnx:
+ try:
+ user = self.authmanager.validate_session(req, session)
+ except InvalidSession:
+ # invalid session
+ self.close_session(session)
+ raise
+ # associate the connection to the current request
+ req.set_session(session, user)
return session
- def open_session(self, req):
+ def open_session(self, req, allow_no_cnx=True):
"""open and return a new session for the given request. The session is
also bound to the request.
raise :exc:`cubicweb.AuthenticationError` if authentication failed
(no authentication info found or wrong user/password)
"""
- cnx, login = self.authmanager.authenticate(req)
- session = DBAPISession(cnx, login)
+ try:
+ cnx, login = self.authmanager.authenticate(req)
+ except AuthenticationError:
+ if allow_no_cnx:
+ session = DBAPISession(None)
+ else:
+ raise
+ else:
+ session = DBAPISession(cnx, login)
self._sessions[session.sessionid] = session
# associate the connection to the current request
req.set_session(session)
@@ -89,15 +97,16 @@
args = req.form
for forminternal_key in ('__form_id', '__domid', '__errorurl'):
args.pop(forminternal_key, None)
- args['__message'] = req._('welcome %s !') % req.user.login
- if 'vid' in req.form:
- args['vid'] = req.form['vid']
- if 'rql' in req.form:
- args['rql'] = req.form['rql']
path = req.relative_path(False)
if path == 'login':
path = 'view'
- raise Redirect(req.build_url(path, **args))
+ args['__message'] = req._('welcome %s !') % req.user.login
+ if 'vid' in req.form:
+ args['vid'] = req.form['vid']
+ if 'rql' in req.form:
+ args['rql'] = req.form['rql']
+ raise Redirect(req.build_url(path, **args))
+ req.set_message(req._('welcome %s !') % req.user.login)
def _update_last_login_time(self, req):
# XXX should properly detect missing permission / non writeable source
@@ -120,10 +129,11 @@
"""
self.info('closing http session %s' % session.sessionid)
del self._sessions[session.sessionid]
- try:
- session.cnx.close()
- except:
- # already closed, may occurs if the repository session expired but
- # not the web session
- pass
- session.cnx = None
+ if session.cnx:
+ try:
+ session.cnx.close()
+ except:
+ # already closed, may occur if the repository session expired
+ # but not the web session
+ pass
+ session.cnx = None
--- a/web/views/sparql.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/sparql.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -77,17 +77,22 @@
YAMS_XMLSCHEMA_MAPPING = {
'String': 'string',
+
+ 'Boolean': 'boolean',
'Int': 'integer',
'Float': 'float',
- 'Boolean': 'boolean',
+
'Datetime': 'dateTime',
+ 'TZDatetime': 'dateTime',
'Date': 'date',
'Time': 'time',
+ 'TZTime': 'time',
+
# XXX the following types don't have direct mapping
'Decimal': 'string',
'Interval': 'duration',
+ 'Bytes': 'base64Binary',
'Password': 'string',
- 'Bytes': 'base64Binary',
}
def xmlschema(yamstype):
@@ -118,7 +123,7 @@
def cell_binding(self, row, col, varname):
celltype = self.cw_rset.description[row][col]
if self._cw.vreg.schema.eschema(celltype).final:
- cellcontent = self.view('cell', self.cw_rset, row=row, col=col)
+ cellcontent = self._cw.view('cell', self.cw_rset, row=row, col=col)
return E.binding(E.literal(cellcontent,
datatype=xmlschema(celltype)),
name=varname)
--- a/web/views/tabs.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/tabs.py Wed Jul 20 18:21:47 2011 +0200
@@ -46,7 +46,7 @@
reloadable=False, show_spinbox=True, w=None):
"""a lazy version of wview"""
w = w or self.w
- self._cw.add_js('cubicweb.lazy.js')
+ self._cw.add_js('cubicweb.ajax.js')
urlparams = self._cw.form.copy()
urlparams.update({'vid' : vid, 'fname' : 'view'})
if rql:
@@ -127,8 +127,8 @@
if entity and len(self.cw_rset) > 1:
entity.view(default, w=self.w)
return
- self._cw.add_css('ui.tabs.css')
- self._cw.add_js(('ui.core.js', 'ui.tabs.js', 'cubicweb.ajax.js'))
+ self._cw.add_css('jquery.ui.css')
+ self._cw.add_js(('jquery.ui.js', 'cubicweb.ajax.js'))
# prune tabs : not all are to be shown
tabs, active_tab = self.prune_tabs(tabs, default)
# build the html structure
@@ -148,7 +148,6 @@
if domid == active_tab:
active_tab_idx = i
w(u'</ul>')
- w(u'</div>')
for tabid, domid, tabkwargs in tabs:
w(u'<div id="%s">' % domid)
tabkwargs.setdefault('tabid', domid)
@@ -156,11 +155,12 @@
tabkwargs.setdefault('rset', self.cw_rset)
self.lazyview(**tabkwargs)
w(u'</div>')
+ w(u'</div>')
# call the setTab() JS function *after* each tab is generated
# because the callback binding needs to be done before
# XXX make work history: true
self._cw.add_onload(u"""
- jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s });
+ jQuery('#entity-tabs-%(eeid)s').tabs( { selected: %(tabindex)s });
setTab('%(domid)s', '%(cookiename)s');
""" % {'tabindex' : active_tab_idx,
'domid' : active_tab,
--- a/web/views/workflow.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/workflow.py Wed Jul 20 18:21:47 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -30,7 +30,7 @@
from logilab.mtconverter import xml_escape
from logilab.common.graph import escape
-from cubicweb import Unauthorized, view
+from cubicweb import Unauthorized
from cubicweb.selectors import (has_related_entities, one_line_rset,
relation_possible, match_form_params,
score_entity, is_instance, adaptable)
@@ -39,7 +39,7 @@
from cubicweb.web import uicfg, stdmsgs, action, component, form, action
from cubicweb.web import formfields as ff, formwidgets as fwdgs
from cubicweb.web.views import TmpFileViewMixin
-from cubicweb.web.views import forms, primary, autoform, ibreadcrumbs
+from cubicweb.web.views import forms, primary, ibreadcrumbs
from cubicweb.web.views.tabs import TabbedPrimaryView, PrimaryTab
from cubicweb.web.views.dotgraphview import DotGraphView, DotPropsHandler
@@ -72,11 +72,7 @@
_abaa.tag_object_of(('WorkflowTransition', 'transition_of', 'Workflow'), True)
_afs = uicfg.autoform_section
-_afs.tag_subject_of(('TrInfo', 'to_state', '*'), 'main', 'hidden')
-_afs.tag_subject_of(('TrInfo', 'from_state', '*'), 'main', 'hidden')
-_afs.tag_attribute(('TrInfo', 'tr_count'), 'main', 'hidden')
-_afs.tag_object_of(('State', 'allowed_transition', '*'), 'main', 'attributes')
-
+_affk = uicfg.autoform_field_kwargs
# IWorkflowable views #########################################################
@@ -90,7 +86,7 @@
fwdgs.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
-class ChangeStateFormView(form.FormViewMixIn, view.EntityView):
+class ChangeStateFormView(form.FormViewMixIn, EntityView):
__regid__ = 'statuschange'
title = _('status change')
__select__ = (one_line_rset()
@@ -114,7 +110,7 @@
def get_form(self, entity, transition, **kwargs):
# XXX used to specify both rset/row/col and entity in case implements
- # selector (and not implements) is used on custom form
+ # selector (and not is_instance) is used on custom form
form = self._cw.vreg['forms'].select(
'changestate', self._cw, entity=entity, transition=transition,
redirect_path=self.redirectpath(entity), **kwargs)
@@ -183,6 +179,15 @@
self.entity.view('wfhistory', w=w, title=None)
+class InContextWithStateView(EntityView):
+ """display incontext view for an entity as well as its current state"""
+ __regid__ = 'incontext-state'
+ __select__ = adaptable('IWorkflowable')
+ def entity_call(self, entity):
+ iwf = entity.cw_adapt_to('IWorkflowable')
+ self.w(u'%s [%s]' % (entity.view('incontext'), iwf.printable_state))
+
+
# workflow actions #############################################################
class WorkflowActions(action.Action):
@@ -242,7 +247,7 @@
default_tab = 'wf_tab_info'
-class CellView(view.EntityView):
+class CellView(EntityView):
__regid__ = 'cell'
__select__ = is_instance('TrInfo')
@@ -250,7 +255,7 @@
self.w(self.cw_rset.get_entity(row, col).view('reledit', rtype='comment'))
-class StateInContextView(view.EntityView):
+class StateInContextView(EntityView):
"""convenience trick, State's incontext view should not be clickable"""
__regid__ = 'incontext'
__select__ = is_instance('State')
@@ -285,7 +290,7 @@
)
-class TransitionSecurityTextView(view.EntityView):
+class TransitionSecurityTextView(EntityView):
__regid__ = 'trsecurity'
__select__ = is_instance('Transition')
@@ -303,7 +308,7 @@
u'<br/>'.join((e.dc_title() for e
in entity.condition))))
-class TransitionAllowedTextView(view.EntityView):
+class TransitionAllowedTextView(EntityView):
__regid__ = 'trfromstates'
__select__ = is_instance('Transition')
@@ -315,51 +320,56 @@
# workflow entity types edition ################################################
-_afs = uicfg.autoform_section
+def _wf_items_for_relation(req, wfeid, wfrelation, field):
+ wf = req.entity_from_eid(wfeid)
+ rschema = req.vreg.schema[field.name]
+ param = 'toeid' if field.role == 'subject' else 'fromeid'
+ return sorted((e.view('combobox'), e.eid)
+ for e in getattr(wf, 'reverse_%s' % wfrelation)
+ if rschema.has_perm(req, 'add', **{param: e.eid}))
+
+# TrInfo
_afs.tag_subject_of(('TrInfo', 'to_state', '*'), 'main', 'hidden')
_afs.tag_subject_of(('TrInfo', 'from_state', '*'), 'main', 'hidden')
-_afs.tag_object_of(('State', 'allowed_transition', '*'), 'main', 'attributes')
-_afs.tag_subject_of(('State', 'allowed_transition', '*'), 'main', 'attributes')
+_afs.tag_attribute(('TrInfo', 'tr_count'), 'main', 'hidden')
+
+# BaseTransition
+# XXX * allowed_transition BaseTransition
+# XXX BaseTransition destination_state *
+
+def transition_states_vocabulary(form, field):
+ entity = form.edited_entity
+ if not entity.has_eid():
+ eids = entity.linked_to('transition_of', 'subject')
+ if not eids:
+ return []
+ return _wf_items_for_relation(form._cw, eids[0], 'state_of', field)
+ return ff.relvoc_unrelated(entity, field.name, field.role)
-def workflow_items_for_relation(req, wfeid, wfrelation, targetrelation):
- wf = req.entity_from_eid(wfeid)
- rschema = req.vreg.schema[targetrelation]
- return sorted((e.view('combobox'), e.eid)
- for e in getattr(wf, 'reverse_%s' % wfrelation)
- if rschema.has_perm(req, 'add', toeid=e.eid))
+_afs.tag_subject_of(('*', 'destination_state', '*'), 'main', 'attributes')
+_affk.tag_subject_of(('*', 'destination_state', '*'),
+ {'choices': transition_states_vocabulary})
+_afs.tag_object_of(('*', 'allowed_transition', '*'), 'main', 'attributes')
+_affk.tag_object_of(('*', 'allowed_transition', '*'),
+ {'choices': transition_states_vocabulary})
+
+# State
+
+def state_transitions_vocabulary(form, field):
+ entity = form.edited_entity
+ if not entity.has_eid():
+ eids = entity.linked_to('state_of', 'subject')
+ if eids:
+ return _wf_items_for_relation(form._cw, eids[0], 'transition_of', field)
+ return []
+ return ff.relvoc_unrelated(entity, field.name, field.role)
+
+_afs.tag_subject_of(('State', 'allowed_transition', '*'), 'main', 'attributes')
+_affk.tag_subject_of(('State', 'allowed_transition', '*'),
+ {'choices': state_transitions_vocabulary})
-class TransitionEditionForm(autoform.AutomaticEntityForm):
- __select__ = is_instance('Transition')
-
- def workflow_states_for_relation(self, targetrelation):
- eids = self.edited_entity.linked_to('transition_of', 'subject')
- if eids:
- return workflow_items_for_relation(self._cw, eids[0], 'state_of',
- targetrelation)
- return []
-
- def subject_destination_state_vocabulary(self, rtype, limit=None):
- if not self.edited_entity.has_eid():
- return self.workflow_states_for_relation('destination_state')
- return self.subject_relation_vocabulary(rtype, limit)
-
- def object_allowed_transition_vocabulary(self, rtype, limit=None):
- if not self.edited_entity.has_eid():
- return self.workflow_states_for_relation('allowed_transition')
- return self.object_relation_vocabulary(rtype, limit)
-
-
-class StateEditionForm(autoform.AutomaticEntityForm):
- __select__ = is_instance('State')
-
- def subject_allowed_transition_vocabulary(self, rtype, limit=None):
- if not self.edited_entity.has_eid():
- eids = self.edited_entity.linked_to('state_of', 'subject')
- if eids:
- return workflow_items_for_relation(self._cw, eids[0], 'transition_of',
- 'allowed_transition')
- return []
+# adaptaters ###################################################################
class WorkflowIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
__select__ = is_instance('Workflow')
@@ -434,7 +444,7 @@
return WorkflowDotPropsHandler(self._cw)
-class TmpPngView(TmpFileViewMixin, view.EntityView):
+class TmpPngView(TmpFileViewMixin, EntityView):
__regid__ = 'tmppng'
__select__ = match_form_params('tmpfile')
content_type = 'image/png'
--- a/web/views/xmlrss.py Mon May 16 16:24:00 2011 +0200
+++ b/web/views/xmlrss.py Wed Jul 20 18:21:47 2011 +0200
@@ -42,6 +42,8 @@
'Date': lambda x: x.strftime('%Y-%m-%d'),
'Datetime': lambda x: x.strftime('%Y-%m-%d %H:%M:%S'),
'Time': lambda x: x.strftime('%H:%M:%S'),
+ 'TZDatetime': lambda x: x.strftime('%Y-%m-%d %H:%M:%S'), # XXX TZ
+ 'TZTime': lambda x: x.strftime('%H:%M:%S'),
'Interval': lambda x: x.days * 60*60*24 + x.seconds,
}